docs: def from_args(cls, args=None, skip_dir_check=False):
          """
          Construct a Fetcher from given command line arguments.
  
          @type args: list(str)
          @param args: Command line arguments (optional). Default is to use args from sys.argv
  
          @type skip_dir_check: bool
          @param skip_dir_check: Boolean identifying whether to check for existing build directory
  
          @rtype: tuple(Fetcher, output path)
          @return: Returns a Fetcher object and keyword arguments for extract_build.
          """
          parser = argparse.ArgumentParser()
          parser.set_defaults(target='firefox', build='latest', tests=None)  # branch default is set after parsing
  
          target_group = parser.add_argument_group('Target')
          target_group.add_argument('--target', choices=sorted(cls.TARGET_CHOICES),
                                    help=('Specify the build target. (default: %(default)s)'))
          target_group.add_argument('--os', choices=sorted(Platform.SUPPORTED),
                                    help=('Specify the target system. (default: ' + std_platform.system() + ')'))
          cpu_choices = sorted(set(itertools.chain(itertools.chain.from_iterable(Platform.SUPPORTED.values()),
                                                   Platform.CPU_ALIASES)))
          target_group.add_argument('--cpu', choices=cpu_choices,
                                    help=('Specify the target CPU. (default: ' + std_platform.machine() + ')'))
  
          type_group = parser.add_argument_group('Build')
          type_group.add_argument('--build', metavar='DATE|REV|NS',
                                  help='Specify the build to download, (default: %(default)s)'
                                       ' Accepts values in format YYYY-MM-DD (2017-01-01)'
                                       ' revision (57b37213d81150642f5139764e7044b07b9dccc3)'
                                       ' or TaskCluster namespace (gecko.v2....)')
  
          branch_group = parser.add_argument_group('Branch')
          branch_args = branch_group.add_mutually_exclusive_group()
          branch_args.add_argument('--inbound', action='store_const', const='inbound', dest='branch',
                                   help='Download from mozilla-inbound')
          branch_args.add_argument('--central', action='store_const', const='central', dest='branch',
                                   help='Download from mozilla-central (default)')
          branch_args.add_argument('--release', action='store_const', const='release', dest='branch',
                                   help='Download from mozilla-release')
          branch_args.add_argument('--beta', action='store_const', const='beta', dest='branch',
                                   help='Download from mozilla-beta')
          branch_args.add_argument('--esr52', action='store_const', const='esr52', dest='branch',
                                   help='Download from mozilla-esr52')
          branch_args.add_argument('--esr', action='store_const', const='esr60', dest='branch',
                                   help='Download from mozilla-esr60')
  
          build_group = parser.add_argument_group('Build Arguments')
          build_group.add_argument('-d', '--debug', action='store_true',
                                   help='Get debug builds w/ symbols (default=optimized).')
          build_group.add_argument('-a', '--asan', action='store_true',
                                   help='Download AddressSanitizer builds.')
          build_group.add_argument('--fuzzing', action='store_true',
                                   help='Download --enable-fuzzing builds.')
          build_group.add_argument('--coverage', action='store_true',
                                   help='Download --coverage builds. This also pulls down the *.gcno files')
          build_group.add_argument('--valgrind', action='store_true',
                                   help='Download Valgrind builds.')
  
          test_group = parser.add_argument_group('Test Arguments')
          test_group.add_argument('--tests', nargs='+', metavar='', choices=cls.TEST_CHOICES,
                                  help=('Download tests associated with this build. Acceptable values are: ' +
                                        ', '.join(cls.TEST_CHOICES)))
          test_group.add_argument('--full-symbols', action='store_true',
                                  help='Download the full crashreport-symbols.zip archive.')
  
          misc_group = parser.add_argument_group('Misc. Arguments')
          misc_group.add_argument('-n', '--name',
                                  help='Specify a name (default=auto)')
          misc_group.add_argument('-o', '--out', default=os.getcwd(),
                                  help='Specify output directory (default=.)')
          misc_group.add_argument('--dry-run', action='store_true',
                                  help="Search for build and output metadata only, don't download anything.")
  
          args = parser.parse_args(args=args)
  
          if re.match(r'(\d{4}-\d{2}-\d{2}|[0-9A-Fa-f]{40}|latest)$', args.build) is None:
              # this is a custom build
              # ensure conflicting options are not set
              if args.branch is not None:
                  parser.error('Cannot specify --build namespace and branch argument: %s' % args.branch)
              if args.debug:
                  parser.error('Cannot specify --build namespace and --debug')
              if args.asan:
                  parser.error('Cannot specify --build namespace and --asan')
              if args.fuzzing:
                  parser.error('Cannot specify --build namespace and --fuzzing')
              if args.coverage:
                  parser.error('Cannot specify --build namespace and --coverage')
              if args.valgrind:
                  parser.error('Cannot specify --build namespace and --valgrind')
  
          # do this default manually so we can error if combined with --build namespace
          # parser.set_defaults(branch='central')
          elif args.branch is None:
              args.branch = 'central'
  
          flags = BuildFlags(args.asan, args.debug, args.fuzzing, args.coverage, args.valgrind)
          obj = cls(args.target, args.branch, args.build, flags, Platform(args.os, args.cpu))
  
          if args.name is None:
              args.name = obj.get_auto_name()
  
          final_dir = os.path.realpath(os.path.join(args.out, args.name))
          if not skip_dir_check and os.path.exists(final_dir):
              parser.error('Folder exists: %s .. exiting' % final_dir)
  
          extract_options = {
              'dry_run': args.dry_run,
              'out': final_dir,
              'full_symbols': args.full_symbols,
              'tests': args.tests
          }
  
          return obj, extract_options