docs: def _build_app(args, extra_args):
      """Builds an app or applet and returns the resulting executable ID
      (unless it was a dry-run, in which case None is returned).
  
      TODO: remote app builds still return None, but we should fix this.
  
      """
  
      if not args.remote:
          # LOCAL BUILD
  
          try:
              output = build_and_upload_locally(
                  args.src_dir,
                  args.mode,
                  overwrite=args.overwrite,
                  archive=args.archive,
                  publish=args.publish,
                  destination_override=args.destination,
                  version_override=args.version_override,
                  bill_to_override=args.bill_to,
                  use_temp_build_project=args.use_temp_build_project,
                  do_parallel_build=args.parallel_build,
                  do_version_autonumbering=args.version_autonumbering,
                  do_try_update=args.update,
                  dx_toolkit_autodep=args.dx_toolkit_autodep,
                  do_check_syntax=args.check_syntax,
                  ensure_upload=args.ensure_upload,
                  force_symlinks=args.force_symlinks,
                  dry_run=args.dry_run,
                  confirm=args.confirm,
                  return_object_dump=args.json,
                  region=args.region,
                  **extra_args
                  )
  
              if output is not None and args.run is None:
                  print(json.dumps(output))
          except dxpy.app_builder.AppBuilderException as e:
              # AppBuilderException represents errors during app or applet building
              # that could reasonably have been anticipated by the user.
              print("Error: %s" % (e.args,), file=sys.stderr)
              sys.exit(3)
          except dxpy.exceptions.DXAPIError as e:
              print("Error: %s" % (e,), file=sys.stderr)
              sys.exit(3)
  
          if args.dry_run:
              return None
  
          return output['id']
  
      else:
          # REMOTE BUILD
  
          try:
              app_json = _parse_app_spec(args.src_dir)
              _check_suggestions(app_json, publish=args.publish)
              _verify_app_source_dir(args.src_dir, args.mode)
              if args.mode == "app" and not args.dry_run:
                  dxpy.executable_builder.verify_developer_rights('app-' + app_json['name'])
          except dxpy.app_builder.AppBuilderException as e:
              print("Error: %s" % (e.args,), file=sys.stderr)
              sys.exit(3)
  
          # The following flags might be useful in conjunction with
          # --remote. To enable these, we need to learn how to pass these
          # options through to the interior call of dx_build_app(let).
          if args.dry_run:
              parser.error('--remote cannot be combined with --dry-run')
          if args.overwrite:
              parser.error('--remote cannot be combined with --overwrite/-f')
          if args.archive:
              parser.error('--remote cannot be combined with --archive/-a')
  
          # The following flags are probably not useful in conjunction
          # with --remote.
          if args.json:
              parser.error('--remote cannot be combined with --json')
          if not args.use_temp_build_project:
              parser.error('--remote cannot be combined with --no-temp-build-project')
  
          if isinstance(args.region, list) and len(args.region) > 1:
              parser.error('--region can only be specified once for remote builds')
          region = args.region[0] if args.region is not None else None
  
          more_kwargs = {}
          if args.version_override:
              more_kwargs['version_override'] = args.version_override
          if args.bill_to:
              more_kwargs['bill_to_override'] = args.bill_to
          if not args.version_autonumbering:
              more_kwargs['do_version_autonumbering'] = False
          if not args.update:
              more_kwargs['do_try_update'] = False
          if not args.parallel_build:
              more_kwargs['do_parallel_build'] = False
          if not args.check_syntax:
              more_kwargs['do_check_syntax'] = False
  
          return _build_app_remote(args.mode, args.src_dir, destination_override=args.destination,
                                   publish=args.publish, dx_toolkit_autodep=args.dx_toolkit_autodep,
                                   region=region, watch=args.watch, **more_kwargs)