예제 #1
0
def make_docker_context(get_steps_fn,
                        github_project,
                        opts=None,
                        default_context_dir=None):
    '''
    Returns a path to the Docker context directory. See parse_args.py.

    Helper for making a command-line utility that writes your project's
    Dockerfile and associated data into a (temporary) directory.  Your main
    program might look something like this:

        print(make_docker_context(
            lambda builder: [builder.step(...), ...],
            'facebook/your_project',
        ))
    '''

    if opts is None:
        opts = {}

    valid_versions = (('ubuntu:14.04', '4.9'), ('ubuntu:16.04', '5'),
                      ('debian:8.6', '4.9'))

    def add_args(parser):
        parser.add_argument(
            '--docker-context-dir',
            metavar='DIR',
            default=default_context_dir,
            help='Write the Dockerfile and its context into this directory. '
            'If empty, make a temporary directory. Default: %(default)s.',
        )
        parser.add_argument(
            '--user',
            metavar='NAME',
            default=opts.get('user', 'nobody'),
            help='Build and install as this user. Default: %(default)s.',
        )
        parser.add_argument(
            '--prefix',
            metavar='DIR',
            default=opts.get('prefix', '/home/install'),
            help='Install all libraries in this prefix. Default: %(default)s.',
        )
        parser.add_argument(
            '--projects-dir',
            metavar='DIR',
            default=opts.get('projects_dir', '/home'),
            help='Place project code directories here. Default: %(default)s.',
        )
        parser.add_argument(
            '--os-image',
            metavar='IMG',
            choices=zip(*valid_versions)[0],
            default=opts.get('os_image', 'debian:8.6'),
            help='Docker OS image -- be sure to use only ones you trust (See '
            'README.docker). Choices: %(choices)s. Default: %(default)s.',
        )
        parser.add_argument(
            '--gcc-version',
            metavar='VER',
            choices=set(zip(*valid_versions)[1]),
            default=opts.get('gcc_version', '4.9'),
            help='Choices: %(choices)s. Default: %(default)s.',
        )
        parser.add_argument(
            '--make-parallelism',
            metavar='NUM',
            type=int,
            default=opts.get('make_parallelism', 1),
            help='Use `make -j` on multi-CPU systems with lots of RAM. '
            'Default: %(default)s.',
        )
        parser.add_argument(
            '--local-repo-dir',
            metavar='DIR',
            help='If set, build {0} from a local directory instead of Github.'.
            format(github_project),
        )

    opts = parse_args_to_fbcode_builder_opts(
        add_args,
        # These have add_argument() calls, others are set via --option.
        (
            'docker_context_dir',
            'user',
            'prefix',
            'projects_dir',
            'os_image',
            'gcc_version',
            'make_parallelism',
            'local_repo_dir',
        ),
        opts,
        help=textwrap.dedent('''

        Reads `fbcode_builder_config.py` from the current directory, and
        prepares a Docker context directory to build {github_project} and
        its dependencies.  Prints to stdout the path to the context
        directory.

        Pass --option {github_project}:git_hash SHA1 to build something
        other than the master branch from Github.

        Or, pass --option {github_project}:local_repo_dir LOCAL_PATH to
        build from a local repo instead of cloning from Github.

        Usage:
            (cd $(./make_docker_context.py) && docker build . 2>&1 | tee log)

        '''.format(github_project=github_project)),
    )

    # This allows travis_docker_build.sh not to know the main Github project.
    local_repo_dir = opts.pop('local_repo_dir', None)
    if local_repo_dir is not None:
        opts['{0}:local_repo_dir'.format(github_project)] = local_repo_dir

    if (opts.get('os_image'), opts.get('gcc_version')) not in valid_versions:
        raise Exception(
            'Due to 4/5 ABI changes (std::string), we can only use {0}'.format(
                ' / '.join('GCC {1} on {0}'.format(*p)
                           for p in valid_versions)))

    if opts.get('docker_context_dir') is None:
        opts['docker_context_dir'] = tempfile.mkdtemp(prefix='docker-context-')
    elif not os.path.exists(opts.get('docker_context_dir')):
        os.makedirs(opts.get('docker_context_dir'))

    builder = DockerFBCodeBuilder(**opts)
    context_dir = builder.option('docker_context_dir')  # Mark option "in-use"
    # The renderer may also populate some files into the context_dir.
    dockerfile = builder.render(get_steps_fn(builder))

    with os.fdopen(
            os.open(
                os.path.join(context_dir, 'Dockerfile'),
                os.O_RDWR | os.O_CREAT
                | os.O_EXCL,  # Do not overwrite existing files
                0o644,
            ),
            'w') as f:
        f.write(dockerfile)

    return context_dir
예제 #2
0
def make_docker_context(get_steps_fn,
                        github_project,
                        opts=None,
                        default_context_dir=None):
    """
    Returns a path to the Docker context directory. See parse_args.py.

    Helper for making a command-line utility that writes your project's
    Dockerfile and associated data into a (temporary) directory.  Your main
    program might look something like this:

        print(make_docker_context(
            lambda builder: [builder.step(...), ...],
            'facebook/your_project',
        ))
    """

    if opts is None:
        opts = {}

    valid_versions = (
        ("ubuntu:16.04", "5"),
        ("ubuntu:18.04", "7"),
    )

    def add_args(parser):
        parser.add_argument(
            "--docker-context-dir",
            metavar="DIR",
            default=default_context_dir,
            help="Write the Dockerfile and its context into this directory. "
            "If empty, make a temporary directory. Default: %(default)s.",
        )
        parser.add_argument(
            "--user",
            metavar="NAME",
            default=opts.get("user", "nobody"),
            help="Build and install as this user. Default: %(default)s.",
        )
        parser.add_argument(
            "--prefix",
            metavar="DIR",
            default=opts.get("prefix", "/home/install"),
            help="Install all libraries in this prefix. Default: %(default)s.",
        )
        parser.add_argument(
            "--projects-dir",
            metavar="DIR",
            default=opts.get("projects_dir", "/home"),
            help="Place project code directories here. Default: %(default)s.",
        )
        parser.add_argument(
            "--os-image",
            metavar="IMG",
            choices=zip(*valid_versions)[0],
            default=opts.get("os_image", valid_versions[0][0]),
            help="Docker OS image -- be sure to use only ones you trust (See "
            "README.docker). Choices: %(choices)s. Default: %(default)s.",
        )
        parser.add_argument(
            "--gcc-version",
            metavar="VER",
            choices=set(zip(*valid_versions)[1]),
            default=opts.get("gcc_version", valid_versions[0][1]),
            help="Choices: %(choices)s. Default: %(default)s.",
        )
        parser.add_argument(
            "--make-parallelism",
            metavar="NUM",
            type=int,
            default=opts.get("make_parallelism", 1),
            help="Use `make -j` on multi-CPU systems with lots of RAM. "
            "Default: %(default)s.",
        )
        parser.add_argument(
            "--local-repo-dir",
            metavar="DIR",
            help="If set, build {0} from a local directory instead of Github.".
            format(github_project),
        )
        parser.add_argument(
            "--ccache-tgz",
            metavar="PATH",
            help="If set, enable ccache for the build. To initialize the "
            "cache, first try to hardlink, then to copy --cache-tgz "
            "as ccache.tgz into the --docker-context-dir.",
        )

    opts = parse_args_to_fbcode_builder_opts(
        add_args,
        # These have add_argument() calls, others are set via --option.
        (
            "docker_context_dir",
            "user",
            "prefix",
            "projects_dir",
            "os_image",
            "gcc_version",
            "make_parallelism",
            "local_repo_dir",
            "ccache_tgz",
        ),
        opts,
        help=textwrap.dedent("""

        Reads `fbcode_builder_config.py` from the current directory, and
        prepares a Docker context directory to build {github_project} and
        its dependencies.  Prints to stdout the path to the context
        directory.

        Pass --option {github_project}:git_hash SHA1 to build something
        other than the master branch from Github.

        Or, pass --option {github_project}:local_repo_dir LOCAL_PATH to
        build from a local repo instead of cloning from Github.

        Usage:
            (cd $(./make_docker_context.py) && docker build . 2>&1 | tee log)

        """.format(github_project=github_project)),
    )

    # This allows travis_docker_build.sh not to know the main Github project.
    local_repo_dir = opts.pop("local_repo_dir", None)
    if local_repo_dir is not None:
        opts["{0}:local_repo_dir".format(github_project)] = local_repo_dir

    if (opts.get("os_image"), opts.get("gcc_version")) not in valid_versions:
        raise Exception(
            "Due to 4/5 ABI changes (std::string), we can only use {0}".format(
                " / ".join("GCC {1} on {0}".format(*p)
                           for p in valid_versions)))

    if opts.get("docker_context_dir") is None:
        opts["docker_context_dir"] = tempfile.mkdtemp(prefix="docker-context-")
    elif not os.path.exists(opts.get("docker_context_dir")):
        os.makedirs(opts.get("docker_context_dir"))

    builder = DockerFBCodeBuilder(**opts)
    context_dir = builder.option("docker_context_dir")  # Mark option "in-use"
    # The renderer may also populate some files into the context_dir.
    dockerfile = builder.render(get_steps_fn(builder))

    with os.fdopen(
            os.open(
                os.path.join(context_dir, "Dockerfile"),
                os.O_RDWR | os.O_CREAT
                | os.O_EXCL,  # Do not overwrite existing files
                0o644,
            ),
            "w",
    ) as f:
        f.write(dockerfile)

    return context_dir