Example #1
0
def generic_worker_toolchain(config, job, taskdesc):
    run = job["run"]

    worker = taskdesc["worker"] = job["worker"]
    worker["chain-of-trust"] = True

    # Allow the job to specify where artifacts come from, but add
    # public/build if it's not there already.
    artifacts = worker.setdefault("artifacts", [])
    if not artifacts:
        generic_worker_add_artifacts(config, job, taskdesc)

    if job["worker"]["os"] == "windows":
        # There were no caches on generic-worker before bug 1519472, and they cause
        # all sorts of problems with Windows toolchain tasks, disable them until
        # tasks are ready.
        run["use-caches"] = False

    env = worker.setdefault("env", {})
    env.update({
        "MOZ_BUILD_DATE": config.params["moz_build_date"],
        "MOZ_SCM_LEVEL": config.params["level"],
    })

    # Use `mach` to invoke python scripts so in-tree libraries are available.
    if run["script"].endswith(".py"):
        raise NotImplementedError(
            "Python toolchain scripts aren't supported on generic-worker")

    attributes = taskdesc.setdefault("attributes", {})
    attributes["toolchain-artifact"] = run.pop("toolchain-artifact")
    if "toolchain-alias" in run:
        attributes["toolchain-alias"] = run.pop("toolchain-alias")

    digest_data = get_digest_data(config, run, taskdesc)

    if job.get("attributes",
               {}).get("cached_task") is not False and not taskgraph.fast:
        name = taskdesc["label"].replace("{}-".format(config.kind), "", 1)
        taskdesc["cache"] = {
            "type": CACHE_TYPE,
            "name": name,
            "digest-data": digest_data,
        }

    run["using"] = "run-task"

    args = run.pop("arguments", "")
    if args:
        args = " " + shell_quote(*args)

    if job["worker"]["os"] == "windows":
        gecko_path = "%GECKO_PATH%"
    else:
        gecko_path = "$GECKO_PATH"

    run["command"] = "{}/taskcluster/scripts/misc/{}{}".format(
        gecko_path, run.pop("script"), args)

    configure_taskdesc_for_run(config, job, taskdesc, worker["implementation"])
Example #2
0
def generic_worker_spidermonkey(config, job, taskdesc):
    assert job['worker']['os'] == 'windows', 'only supports windows right now'

    run = job['run']

    worker = taskdesc['worker']

    generic_worker_add_artifacts(config, job, taskdesc)
    docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)

    env = worker.setdefault('env', {})
    env.update({
        'MOZHARNESS_DISABLE': 'true',
        'SPIDERMONKEY_VARIANT': run['spidermonkey-variant'],
        'MOZ_BUILD_DATE': config.params['moz_build_date'],
        'MOZ_SCM_LEVEL': config.params['level'],
        'SCCACHE_DISABLE': "1",
        'WORK': ".",  # Override the defaults in build scripts
        'SRCDIR': "./src",  # with values suiteable for windows generic worker
        'UPLOAD_DIR': "./public/build"
    })

    script = "build-sm.sh"
    if run['using'] == 'spidermonkey-package':
        script = "build-sm-package.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-package is not a supported configuration")
    elif run['using'] == 'spidermonkey-mozjs-crate':
        script = "build-sm-mozjs-crate.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-mozjs-crate is not a supported configuration")
    elif run['using'] == 'spidermonkey-rust-bindings':
        script = "build-sm-rust-bindings.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-rust-bindings is not a supported configuration")

    hg_command = ['"c:\\Program Files\\Mercurial\\hg.exe"']
    hg_command.append('robustcheckout')
    hg_command.extend(['--sharebase', 'y:\\hg-shared'])
    hg_command.append('--purge')
    hg_command.extend(['--upstream', 'https://hg.mozilla.org/mozilla-unified'])
    hg_command.extend(['--revision', env['GECKO_HEAD_REV']])
    hg_command.append(env['GECKO_HEAD_REPOSITORY'])
    hg_command.append('.\\src')

    command = [
        'c:\\mozilla-build\\msys\\bin\\bash.exe '  # string concat
        '"./src/taskcluster/scripts/builder/%s"' % script
    ]

    worker['command'] = []
    worker['command'].extend([' '.join(hg_command), ' '.join(command)])
Example #3
0
def generic_worker_spidermonkey(config, job, taskdesc):
    assert job['worker']['os'] == 'windows', 'only supports windows right now'

    run = job['run']

    worker = taskdesc['worker']

    generic_worker_add_artifacts(config, job, taskdesc)
    support_vcs_checkout(config, job, taskdesc)

    env = worker.setdefault('env', {})
    env.update({
        'MOZHARNESS_DISABLE': 'true',
        'SPIDERMONKEY_VARIANT': run['spidermonkey-variant'],
        'MOZ_BUILD_DATE': config.params['moz_build_date'],
        'MOZ_SCM_LEVEL': config.params['level'],
        'SCCACHE_DISABLE': "1",
        'WORK': ".",  # Override the defaults in build scripts
        'SRCDIR': "./src",  # with values suiteable for windows generic worker
        'UPLOAD_DIR': "./public/build"
    })
    if 'spidermonkey-platform' in run:
        env['SPIDERMONKEY_PLATFORM'] = run['spidermonkey-platform']

    script = "build-sm.sh"
    if run['using'] == 'spidermonkey-package':
        script = "build-sm-package.sh"
        # Don't allow untested configurations yet
        raise Exception("spidermonkey-package is not a supported configuration")
    elif run['using'] == 'spidermonkey-mozjs-crate':
        script = "build-sm-mozjs-crate.sh"
        # Don't allow untested configurations yet
        raise Exception("spidermonkey-mozjs-crate is not a supported configuration")
    elif run['using'] == 'spidermonkey-rust-bindings':
        script = "build-sm-rust-bindings.sh"
        # Don't allow untested configurations yet
        raise Exception("spidermonkey-rust-bindings is not a supported configuration")

    hg_command = generic_worker_hg_commands(
        'https://hg.mozilla.org/mozilla-unified',
        env['GECKO_HEAD_REPOSITORY'],
        env['GECKO_HEAD_REV'],
        r'.\src',
    )[0]

    command = ['c:\\mozilla-build\\msys\\bin\\bash.exe '  # string concat
               '"./src/taskcluster/scripts/builder/%s"' % script]

    worker['command'] = [
        hg_command,
        ' '.join(command),
    ]
Example #4
0
def generic_worker_spidermonkey(config, job, taskdesc):
    assert job['worker']['os'] == 'windows', 'only supports windows right now'

    run = job['run']

    worker = taskdesc['worker'] = job['worker']

    generic_worker_add_artifacts(config, job, taskdesc)

    env = worker.setdefault('env', {})
    env.update({
        'MOZHARNESS_DISABLE': 'true',
        'SPIDERMONKEY_VARIANT': run.pop('spidermonkey-variant'),
        'MOZ_BUILD_DATE': config.params['moz_build_date'],
        'MOZ_SCM_LEVEL': config.params['level'],
        'SCCACHE_DISABLE': "1",
        'WORK': ".",  # Override the defaults in build scripts
        'GECKO_PATH':
        "./src",  # with values suiteable for windows generic worker
        'UPLOAD_DIR': "./public/build"
    })
    if 'spidermonkey-platform' in run:
        env['SPIDERMONKEY_PLATFORM'] = run.pop('spidermonkey-platform')

    script = "build-sm.sh"
    if run['using'] == 'spidermonkey-package':
        script = "build-sm-package.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-package is not a supported configuration")
    elif run['using'] == 'spidermonkey-mozjs-crate':
        script = "build-sm-mozjs-crate.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-mozjs-crate is not a supported configuration")
    elif run['using'] == 'spidermonkey-rust-bindings':
        script = "build-sm-rust-bindings.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-rust-bindings is not a supported configuration")

    run['using'] = 'run-task'
    run['command'] = [
        'c:\\mozilla-build\\msys\\bin\\bash.exe '  # string concat
        '"./src/taskcluster/scripts/builder/%s"' % script
    ]

    configure_taskdesc_for_run(config, job, taskdesc, worker['implementation'])
Example #5
0
def generic_worker_spidermonkey(config, job, taskdesc):
    assert job["worker"]["os"] == "windows", "only supports windows right now"

    run = job["run"]

    worker = taskdesc["worker"] = job["worker"]

    generic_worker_add_artifacts(config, job, taskdesc)

    env = worker.setdefault("env", {})
    env.update({
        "MOZHARNESS_DISABLE": "true",
        "SPIDERMONKEY_VARIANT": run.pop("spidermonkey-variant"),
        "MOZ_BUILD_DATE": config.params["moz_build_date"],
        "MOZ_SCM_LEVEL": config.params["level"],
        "SCCACHE_DISABLE": "1",
        "WORK": ".",  # Override the defaults in build scripts
        "GECKO_PATH":
        "./src",  # with values suiteable for windows generic worker
        "UPLOAD_DIR": "./public/build",
    })
    if "spidermonkey-platform" in run:
        env["SPIDERMONKEY_PLATFORM"] = run.pop("spidermonkey-platform")

    script = "build-sm.sh"
    if run["using"] == "spidermonkey-package":
        script = "build-sm-package.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-package is not a supported configuration")
    elif run["using"] == "spidermonkey-mozjs-crate":
        script = "build-sm-mozjs-crate.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-mozjs-crate is not a supported configuration")
    elif run["using"] == "spidermonkey-rust-bindings":
        script = "build-sm-rust-bindings.sh"
        # Don't allow untested configurations yet
        raise Exception(
            "spidermonkey-rust-bindings is not a supported configuration")

    run["using"] = "run-task"
    run["command"] = [
        "c:\\mozilla-build\\msys\\bin\\bash.exe "  # string concat
        '"./src/taskcluster/scripts/builder/%s"' % script
    ]

    configure_taskdesc_for_run(config, job, taskdesc, worker["implementation"])
Example #6
0
def mozharness_on_generic_worker(config, job, taskdesc):
    assert (job["worker"]["os"] == "windows"
            ), "only supports windows right now: {}".format(job["label"])

    run = job['run']

    # fail if invalid run options are included
    invalid = []
    for prop in ['need-xvfb']:
        if prop in run and run.pop(prop):
            invalid.append(prop)
    if not run.pop('keep-artifacts', True):
        invalid.append('keep-artifacts')
    if invalid:
        raise Exception(
            "Jobs run using mozharness on Windows do not support properties " +
            ', '.join(invalid))

    worker = taskdesc['worker'] = job['worker']

    worker['taskcluster-proxy'] = run.pop('taskcluster-proxy', None)

    setup_secrets(config, job, taskdesc)

    taskdesc['worker'].setdefault('artifacts', []).append({
        'name': 'public/logs',
        'path': 'logs',
        'type': 'directory'
    })
    if not worker.get('skip-artifacts', False):
        generic_worker_add_artifacts(config, job, taskdesc)

    env = worker['env']
    env.update({
        'MOZ_BUILD_DATE': config.params['moz_build_date'],
        'MOZ_SCM_LEVEL': config.params['level'],
        'MH_BRANCH': config.params['project'],
        'MOZ_SOURCE_CHANGESET': get_branch_rev(config),
        'MOZ_SOURCE_REPO': get_branch_repo(config),
    })
    if run.pop('use-simple-package'):
        env.update({'MOZ_SIMPLE_PACKAGE_NAME': 'target'})

    extra_config = run.pop('extra-config', {})
    extra_config['objdir'] = 'obj-build'
    env['EXTRA_MOZHARNESS_CONFIG'] = six.ensure_text(json.dumps(extra_config))

    # The windows generic worker uses batch files to pass environment variables
    # to commands.  Setting a variable to empty in a batch file unsets, so if
    # there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
    # mozharness doesn't try to find the commit message on its own.
    if config.params.is_try():
        env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message'

    if not job['attributes']['build_platform'].startswith('win'):
        raise Exception(
            "Task generation for mozharness build jobs currently only supported on Windows"
        )

    mh_command = [
        'c:/mozilla-build/python/python.exe',
        '%GECKO_PATH%/testing/{}'.format(run.pop('script')),
    ]

    for path in run.pop('config-paths', []):
        mh_command.append('--extra-config-path %GECKO_PATH%/{}'.format(path))

    for cfg in run.pop('config'):
        mh_command.append('--config ' + cfg)
    if run.pop('use-magic-mh-args'):
        mh_command.append('--branch ' + config.params['project'])
    mh_command.append(r'--work-dir %cd:Z:=z:%\workspace')
    for action in run.pop('actions', []):
        mh_command.append('--' + action)

    for option in run.pop('options', []):
        mh_command.append('--' + option)
    if run.get('custom-build-variant-cfg'):
        mh_command.append('--custom-build-variant')
        mh_command.append(run.pop('custom-build-variant-cfg'))

    run['using'] = 'run-task'
    run['command'] = mh_command
    run.pop('secrets')
    run.pop('requires-signed-builds')
    run.pop('job-script', None)
    configure_taskdesc_for_run(config, job, taskdesc, worker['implementation'])

    # TODO We should run the mozharness script with `mach python` so these
    # modules are automatically available, but doing so somehow caused hangs in
    # Windows ccov builds (see bug 1543149).
    mozbase_dir = "{}/testing/mozbase".format(env['GECKO_PATH'])
    env['PYTHONPATH'] = ';'.join([
        "{}/manifestparser".format(mozbase_dir),
        "{}/mozinfo".format(mozbase_dir),
        "{}/mozfile".format(mozbase_dir),
        "{}/mozprocess".format(mozbase_dir),
        "{}/third_party/python/six".format(env['GECKO_PATH']),
    ])

    if taskdesc.get('needs-sccache'):
        worker['command'] = [
            # Make the comment part of the first command, as it will help users to
            # understand what is going on, and why these steps are implemented.
            dedent('''\
            :: sccache currently uses the full compiler commandline as input to the
            :: cache hash key, so create a symlink to the task dir and build from
            :: the symlink dir to get consistent paths.
            if exist z:\\build rmdir z:\\build'''),
            r'mklink /d z:\build %cd%',
            # Grant delete permission on the link to everyone.
            r'icacls z:\build /grant *S-1-1-0:D /L',
            r'cd /d z:\build',
        ] + worker['command']
Example #7
0
def mozharness_on_generic_worker(config, job, taskdesc):
    assert job['worker']['os'] == 'windows', 'only supports windows right now'

    run = job['run']

    # fail if invalid run options are included
    invalid = []
    for prop in [
            'tooltool-downloads', 'secrets', 'taskcluster-proxy', 'need-xvfb'
    ]:
        if prop in run and run[prop]:
            invalid.append(prop)
    if not run.get('keep-artifacts', True):
        invalid.append('keep-artifacts')
    if invalid:
        raise Exception(
            "Jobs run using mozharness on Windows do not support properties " +
            ', '.join(invalid))

    worker = taskdesc['worker']

    taskdesc['worker'].setdefault('artifacts', []).append({
        'name': 'public/logs',
        'path': 'logs',
        'type': 'directory'
    })
    if not worker.get('skip-artifacts', False):
        generic_worker_add_artifacts(config, job, taskdesc)
    support_vcs_checkout(config, job, taskdesc)

    env = worker['env']
    env.update({
        'MOZ_BUILD_DATE': config.params['moz_build_date'],
        'MOZ_SCM_LEVEL': config.params['level'],
        'MOZ_AUTOMATION': '1',
        'MH_BRANCH': config.params['project'],
        'MOZ_SOURCE_CHANGESET': env['GECKO_HEAD_REV'],
    })
    if run['use-simple-package']:
        env.update({'MOZ_SIMPLE_PACKAGE_NAME': 'target'})

    if 'extra-config' in run:
        env['EXTRA_MOZHARNESS_CONFIG'] = json.dumps(run['extra-config'])

    # The windows generic worker uses batch files to pass environment variables
    # to commands.  Setting a variable to empty in a batch file unsets, so if
    # there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
    # mozharness doesn't try to find the commit message on its own.
    if config.params.is_try():
        env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message'

    if run['comm-checkout']:
        env['MOZ_SOURCE_CHANGESET'] = env['COMM_HEAD_REV']

    if not job['attributes']['build_platform'].startswith('win'):
        raise Exception(
            "Task generation for mozharness build jobs currently only supported on Windows"
        )

    mh_command = [r'c:\mozilla-build\python\python.exe']
    mh_command.append('\\'.join(
        [r'.\build\src\testing', run['script'].replace('/', '\\')]))

    if 'config-paths' in run:
        for path in run['config-paths']:
            mh_command.append(r'--extra-config-path '
                              r'.\build\src\{}'.format(path.replace('/',
                                                                    '\\')))

    for cfg in run['config']:
        mh_command.append('--config ' + cfg.replace('/', '\\'))
    if run['use-magic-mh-args']:
        mh_command.append('--branch ' + config.params['project'])
    mh_command.append(r'--work-dir %cd:Z:=z:%\build')
    for action in run.get('actions', []):
        mh_command.append('--' + action)

    for option in run.get('options', []):
        mh_command.append('--' + option)
    if run.get('custom-build-variant-cfg'):
        mh_command.append('--custom-build-variant')
        mh_command.append(run['custom-build-variant-cfg'])

    hg_commands = generic_worker_hg_commands(
        base_repo=env['GECKO_BASE_REPOSITORY'],
        head_repo=env['GECKO_HEAD_REPOSITORY'],
        head_rev=env['GECKO_HEAD_REV'],
        path=r'.\build\src',
    )

    if run['comm-checkout']:
        hg_commands.extend(
            generic_worker_hg_commands(base_repo=env['COMM_BASE_REPOSITORY'],
                                       head_repo=env['COMM_HEAD_REPOSITORY'],
                                       head_rev=env['COMM_HEAD_REV'],
                                       path=r'.\build\src\comm'))

    fetch_commands = []
    if 'MOZ_FETCHES' in env:
        # When Bug 1436037 is fixed, run-task can be used for this task,
        # and this call can go away
        fetch_commands.append(' '.join([
            r'c:\mozilla-build\python3\python3.exe',
            r'build\src\taskcluster\scripts\misc\fetch-content',
            'task-artifacts',
        ]))

    worker['command'] = []
    if taskdesc.get('needs-sccache'):
        worker['command'].extend([
            # Make the comment part of the first command, as it will help users to
            # understand what is going on, and why these steps are implemented.
            dedent('''\
            :: sccache currently uses the full compiler commandline as input to the
            :: cache hash key, so create a symlink to the task dir and build from
            :: the symlink dir to get consistent paths.
            if exist z:\\build rmdir z:\\build'''),
            r'mklink /d z:\build %cd%',
            # Grant delete permission on the link to everyone.
            r'icacls z:\build /grant *S-1-1-0:D /L',
            r'cd /d z:\build',
        ])

    worker['command'].extend(hg_commands)
    worker['command'].extend(fetch_commands)
    worker['command'].extend([' '.join(mh_command)])
Example #8
0
def mozharness_on_generic_worker(config, job, taskdesc):
    assert job['worker']['os'] == 'windows', 'only supports windows right now'

    run = job['run']

    # fail if invalid run options are included
    invalid = []
    for prop in [
            'tooltool-downloads', 'secrets', 'taskcluster-proxy', 'need-xvfb'
    ]:
        if prop in run and run[prop]:
            invalid.append(prop)
    if not run.get('keep-artifacts', True):
        invalid.append('keep-artifacts')
    if invalid:
        raise Exception(
            "Jobs run using mozharness on Windows do not support properties " +
            ', '.join(invalid))

    worker = taskdesc['worker']

    generic_worker_add_artifacts(config, job, taskdesc)

    docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)

    env = worker['env']
    env.update({
        'MOZ_BUILD_DATE': config.params['moz_build_date'],
        'MOZ_SCM_LEVEL': config.params['level'],
        'MOZ_AUTOMATION': '1',
    })
    if run['use-simple-package']:
        env.update({'MOZ_SIMPLE_PACKAGE_NAME': 'target'})

    if 'extra-config' in run:
        env['EXTRA_MOZHARNESS_CONFIG'] = json.dumps(run['extra-config'])

    # The windows generic worker uses batch files to pass environment variables
    # to commands.  Setting a variable to empty in a batch file unsets, so if
    # there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
    # mozharness doesn't try to find the commit message on its own.
    if config.params.is_try():
        env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message'

    if not job['attributes']['build_platform'].startswith('win'):
        raise Exception(
            "Task generation for mozharness build jobs currently only supported on Windows"
        )

    mh_command = [r'c:\mozilla-build\python\python.exe']
    mh_command.append('\\'.join(
        [r'.\build\src\testing', run['script'].replace('/', '\\')]))

    if 'config-paths' in run:
        for path in run['config-paths']:
            mh_command.append(r'--extra-config-path '
                              r'.\build\src\{}'.format(path.replace('/',
                                                                    '\\')))

    for cfg in run['config']:
        mh_command.append('--config ' + cfg.replace('/', '\\'))
    if run['use-magic-mh-args']:
        mh_command.append('--branch ' + config.params['project'])
        mh_command.append(r'--skip-buildbot-actions')
    mh_command.append(r'--work-dir %cd:Z:=z:%\build')
    for action in run.get('actions', []):
        assert ' ' not in action
        mh_command.append('--' + action)

    for option in run.get('options', []):
        assert ' ' not in option
        mh_command.append('--' + option)
    if run.get('custom-build-variant-cfg'):
        mh_command.append('--custom-build-variant')
        mh_command.append(run['custom-build-variant-cfg'])

    def checkout_repo(base_repo, head_repo, head_rev, path):
        hg_command = ['"c:\\Program Files\\Mercurial\\hg.exe"']
        hg_command.append('robustcheckout')
        hg_command.extend(['--sharebase', 'y:\\hg-shared'])
        hg_command.append('--purge')
        hg_command.extend(['--upstream', base_repo])
        hg_command.extend(['--revision', head_rev])
        hg_command.append(head_repo)
        hg_command.append(path)

        logging_command = [
            b":: TinderboxPrint:<a href={source_repo}/rev/{revision} "
            b"title='Built from {repo_name} revision {revision}'>{revision}</a>\n"
            .format(
                revision=head_rev,
                source_repo=head_repo,
                repo_name=head_repo.split('/')[-1],
            )
        ]

        return [
            ' '.join(hg_command),
            ' '.join(logging_command),
        ]

    hg_commands = checkout_repo(base_repo=env['GECKO_BASE_REPOSITORY'],
                                head_repo=env['GECKO_HEAD_REPOSITORY'],
                                head_rev=env['GECKO_HEAD_REV'],
                                path='.\\build\\src')

    if run['comm-checkout']:
        hg_commands.extend(
            checkout_repo(base_repo=env['COMM_BASE_REPOSITORY'],
                          head_repo=env['COMM_HEAD_REPOSITORY'],
                          head_rev=env['COMM_HEAD_REV'],
                          path='.\\build\\src\\comm'))

    worker['command'] = []
    if taskdesc.get('needs-sccache'):
        worker['command'].extend([
            # Make the comment part of the first command, as it will help users to
            # understand what is going on, and why these steps are implemented.
            dedent('''\
            :: sccache currently uses the full compiler commandline as input to the
            :: cache hash key, so create a symlink to the task dir and build from
            :: the symlink dir to get consistent paths.
            if exist z:\\build rmdir z:\\build'''),
            r'mklink /d z:\build %cd%',
            # Grant delete permission on the link to everyone.
            r'icacls z:\build /grant *S-1-1-0:D /L',
            r'cd /d z:\build',
        ])

    worker['command'].extend(hg_commands)
    worker['command'].extend([' '.join(mh_command)])
Example #9
0
def mozharness_on_generic_worker(config, job, taskdesc):
    assert job["worker"]["os"] in (
        "windows",
        "macosx",
    ), "only supports windows and macOS right now: {}".format(job["label"])

    run = job["run"]

    # fail if invalid run options are included
    invalid = []
    for prop in ["need-xvfb"]:
        if prop in run and run.pop(prop):
            invalid.append(prop)
    if not run.pop("keep-artifacts", True):
        invalid.append("keep-artifacts")
    if invalid:
        raise Exception(
            "Jobs run using mozharness on Windows do not support properties "
            + ", ".join(invalid)
        )

    worker = taskdesc["worker"] = job["worker"]

    worker["taskcluster-proxy"] = run.pop("taskcluster-proxy", None)

    setup_secrets(config, job, taskdesc)

    taskdesc["worker"].setdefault("artifacts", []).append(
        {"name": "public/logs", "path": "logs", "type": "directory"}
    )
    if not worker.get("skip-artifacts", False):
        generic_worker_add_artifacts(config, job, taskdesc)

    env = worker["env"]
    env.update(
        {
            "MOZ_BUILD_DATE": config.params["moz_build_date"],
            "MOZ_SCM_LEVEL": config.params["level"],
            "MH_BRANCH": config.params["project"],
            "MOZ_SOURCE_CHANGESET": get_branch_rev(config),
            "MOZ_SOURCE_REPO": get_branch_repo(config),
        }
    )
    if run.pop("use-simple-package"):
        env.update({"MOZ_SIMPLE_PACKAGE_NAME": "target"})

    extra_config = run.pop("extra-config", {})
    extra_config["objdir"] = "obj-build"
    env["EXTRA_MOZHARNESS_CONFIG"] = six.ensure_text(
        json.dumps(extra_config, sort_keys=True)
    )

    # The windows generic worker uses batch files to pass environment variables
    # to commands.  Setting a variable to empty in a batch file unsets, so if
    # there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
    # mozharness doesn't try to find the commit message on its own.
    if config.params.is_try():
        env["TRY_COMMIT_MSG"] = config.params["message"] or "no commit message"

    if not job["attributes"]["build_platform"].startswith(("win", "macosx")):
        raise Exception(
            "Task generation for mozharness build jobs currently only supported on "
            "Windows and macOS"
        )

    mh_command = []
    if job["worker"]["os"] == "windows":
        mh_command.append("c:/mozilla-build/python3/python3.exe")
        gecko_path = "%GECKO_PATH%"
    else:
        gecko_path = "$GECKO_PATH"

    mh_command += [
        "{}/mach".format(gecko_path),
        "python",
        "--no-activate",
        "{}/testing/{}".format(gecko_path, run.pop("script")),
    ]

    for path in run.pop("config-paths", []):
        mh_command.append("--extra-config-path {}/{}".format(gecko_path, path))

    for cfg in run.pop("config"):
        mh_command.extend(("--config", cfg))
    if run.pop("use-magic-mh-args"):
        mh_command.extend(("--branch", config.params["project"]))
    if job["worker"]["os"] == "windows":
        mh_command.extend(("--work-dir", r"%cd:Z:=z:%\workspace"))
    for action in run.pop("actions", []):
        mh_command.append("--" + action)

    for option in run.pop("options", []):
        mh_command.append("--" + option)
    if run.get("custom-build-variant-cfg"):
        mh_command.append("--custom-build-variant")
        mh_command.append(run.pop("custom-build-variant-cfg"))

    if job["worker"]["os"] == "macosx":
        # Ideally, we'd use shellutil.quote, but that would single-quote
        # $GECKO_PATH, which would defeat having the variable in the command
        # in the first place, as it wouldn't be expanded.
        # In practice, arguments are expected not to contain characters that
        # would require quoting.
        mh_command = " ".join(mh_command)

    run["using"] = "run-task"
    run["command"] = mh_command
    run.pop("secrets")
    run.pop("requires-signed-builds")
    run.pop("job-script", None)
    configure_taskdesc_for_run(config, job, taskdesc, worker["implementation"])

    # Everything past this point is Windows-specific.
    if job["worker"]["os"] == "macosx":
        return

    if taskdesc.get("use-sccache"):
        worker["command"] = (
            [
                # Make the comment part of the first command, as it will help users to
                # understand what is going on, and why these steps are implemented.
                dedent(
                    """\
            :: sccache currently uses the full compiler commandline as input to the
            :: cache hash key, so create a symlink to the task dir and build from
            :: the symlink dir to get consistent paths.
            if exist z:\\build rmdir z:\\build"""
                ),
                r"mklink /d z:\build %cd%",
                # Grant delete permission on the link to everyone.
                r"icacls z:\build /grant *S-1-1-0:D /L",
                r"cd /d z:\build",
            ]
            + worker["command"]
        )