예제 #1
0
    def _BuildGenerator(
        source_dir,
        configuration,
        generator=_DEFAULT_GENERATOR,
    ):
        temp_dir = CurrentShell.CreateTempDirectory()

        with CallOnExit(lambda: FileSystem.RemoveTree(temp_dir)):
            command_line = 'cmake {generator}-S "{source_dir}" -B "{build_dir}" -DCppCommon_CMAKE_DEBUG_OUTPUT=On -DCMAKE_BUILD_TYPE={config}'.format(
                generator='-G "{}" '.format(generator) if generator else "",
                source_dir=source_dir,
                build_dir=temp_dir,
                config=configuration,
            )

            result, output = Process.Execute(command_line)

            if result == 0:
                result, output = Process.Execute('cmake --build "{}"'.format(temp_dir))

            yield temp_dir, result, output
예제 #2
0
    def Build( force=False,
               no_squash=False,
               keep_temporary_image=False,
               output_stream=sys.stdout,
               preserve_ansi_escape_sequences=False,
             ):
        with StreamDecorator.GenerateAnsiSequenceStream( output_stream,
                                                         preserve_ansi_escape_sequences=preserve_ansi_escape_sequences,
                                                       ) as output_stream:
            with StreamDecorator(output_stream).DoneManager( line_prefix='',
                                                             prefix="\nResults: ",
                                                             suffix='\n',
                                                           ) as dm:
                if not _VerifyDocker():
                    dm.stream.write("ERROR: Ensure that docker is installed and available within this environment.\n")
                    dm.result = -1

                    return dm.result

                output_dir = os.path.join(calling_dir, "Generated")

                source_dir = os.path.join(output_dir, "Source")
                base_image_dir = os.path.join(output_dir, "Images", "Base")
                activated_image_dir = os.path.join(output_dir, "Images", "Activated")

                image_code_base = "/usr/lib/CommonEnvironmentImage"
                image_code_dir = "{}/{}".format( image_code_base,
                                                 repository_name.replace('_', '/'),
                                               )

                if no_now_tag:
                    now_tag = None
                else:
                    now = time.localtime()
                    now_tag = "{0}.{1:02d}.{2:02d}".format(now[0], now[1], now[2])
                    
                # Create the base image
                dm.stream.write("Creating base image...")
                with dm.stream.DoneManager(suffix='\n') as base_dm:
                    FileSystem.MakeDirs(base_image_dir)

                    # Get the source
                    scm = GetAnySCM(calling_dir)
                    
                    if not os.path.isdir(source_dir):
                        base_dm.stream.write("Cloning source...")
                        with base_dm.stream.DoneManager() as this_dm:
                            # Ensure that the parent dir exists, but don't create the dir iteself.
                            FileSystem.MakeDirs(os.path.dirname(source_dir))
                    
                            # Enlist in the repo. 
                            temp_dir = CurrentShell.CreateTempDirectory()
                            FileSystem.RemoveTree(temp_dir)
                    
                            this_dm.result, output = scm.Clone(repository_uri, temp_dir)
                            if this_dm.result != 0:
                                this_dm.stream.write(output)
                                return this_dm.result
                    
                            os.rename(temp_dir, source_dir)
                    
                        has_changes = True
                    else:
                        # The repo exists
                        base_dm.stream.write("Updating source...")
                        with base_dm.stream.DoneManager() as this_dm:
                            this_dm.result, output = scm.Pull(source_dir)
                            if this_dm.result != 0:
                                this_dm.stream.write(output)
                                return this_dm.result
                    
                            has_changes = True
                    
                            if scm.Name == "Mercurial":
                                if "no changes found" in output:
                                    has_changes = False
                            elif scm.Name == "Git":
                                if "Already up-to-date" in output:
                                    has_changes = False
                            else:
                                assert False, "Unsupported SCM: {}".format(scm.Name)
                    
                            if has_changes:
                                this_dm.result, output = scm.Update(source_dir)
                                if this_dm.result != 0:
                                    this_dm.stream.write(output)
                                    return this_dm.result
                    
                    # Filter the source
                    filtered_source_dir = os.path.join(base_image_dir, "FilteredSource")

                    if os.path.isdir(filtered_source_dir) and not force and not has_changes:
                        base_dm.stream.write("No source changes were detected.\n")
                    else:
                        with base_dm.stream.SingleLineDoneManager( "Filtering source...",
                                                                 ) as this_dm:
                            temp_dir = CurrentShell.CreateTempDirectory()
                            FileSystem.RemoveTree(temp_dir)
                    
                            FileSystem.CopyTree( source_dir,
                                                 temp_dir,
                                                 excludes=[ "/.git",
                                                            "/.gitignore",
                                                            "/.hg",
                                                            "/.hgignore",
                    
                                                            "*/Generated",
                                                            "*/__pycache__",
                                                            "*/Windows",
                                                            "/*/src",
                    
                                                            "*.cmd",
                                                            "*.ps1",
                                                            "*.pyc",
                                                            "*.pyo",
                                                          ],
                                                 optional_output_stream=this_dm.stream,
                                               )
                    
                            FileSystem.RemoveTree(filtered_source_dir)
                    
                            os.rename(temp_dir, filtered_source_dir)
                    
                    base_dm.stream.write("Verifying Docker base image...")
                    with base_dm.stream.DoneManager() as this_dm:
                        this_dm.result, output = Process.Execute('docker image history "{}"'.format(base_docker_image))
                        if this_dm.result != 0:
                            this_dm.stream.write(output)
                            return this_dm.result
                    
                    base_dm.stream.write("Creating dockerfile...")
                    with base_dm.stream.DoneManager():
                        setup_statement = "./Setup.sh{}".format('' if not repository_setup_configurations else ' {}'.format(' '.join([ '"/configuration={}"'.format(configuration) for configuration in repository_setup_configurations ])))
                    
                        if repository_name == "Common_Environment":
                            commands = textwrap.dedent(
                                            """\
                                            RUN link /usr/bin/python3 /usr/bin/python
                    
                                            RUN adduser --disabled-password --disabled-login --gecos "" "{username}" \\
                                             && addgroup "{groupname}" \\
                                             && adduser "{username}" "{groupname}"
                    
                                            RUN cd {image_code_dir} \\
                                             && {setup_statement}
                    
                                            """).format( username=image_username,
                                                         groupname=image_groupname,
                                                         image_code_dir=image_code_dir,
                                                         setup_statement=setup_statement,
                                                       )
                        else:
                            import io
                    
                            with io.open( os.path.join(base_image_dir, "SetupEnvironmentImpl.sh"),
                                          'w',
                                          newline='\n',
                                        ) as f:
                                f.write(textwrap.dedent(
                                            """\
                                            #!/bin/bash
                                            . {image_code_base}/Common/Environment/Activate.sh python36
                                            cd {image_code_dir}
                                            {setup_statement}
                                            rm --recursive {image_code_base}/Common/Environment/Generated/Linux/Default
                                            """).format( image_code_base=image_code_base,
                                                         image_code_dir=image_code_dir,
                                                         setup_statement=setup_statement,
                                                       ))
                    
                            commands = textwrap.dedent(
                                            """\
                                            COPY SetupEnvironmentImpl.sh /tmp/SetupEnvironmentImpl.sh
                    
                                            RUN chmod a+x /tmp/SetupEnvironmentImpl.sh \\
                                             && /tmp/SetupEnvironmentImpl.sh
                                            """)
                    
                        with open(os.path.join(base_image_dir, "Dockerfile"), 'w') as f:
                            f.write(textwrap.dedent(
                                """\
                                FROM {base_image}
                    
                                COPY FilteredSource {image_code_dir}
                    
                                {commands}
                    
                                RUN chown -R {username}:{groupname} {image_code_dir} \\
                                 && chmod g-s {image_code_dir}/Generated/Linux \\
                                 && chmod 0750 {image_code_dir}/Generated/Linux \\
                                 && chmod -R o-rwx {image_code_dir}
                    
                                # Cleanup
                                RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
                    
                                LABEL maintainer="{maintainer}"
                    
                                # By default, run a bash prompt as the source code user
                                WORKDIR {image_code_dir}
                                CMD [ "/sbin/my_init", "/sbin/setuser", "{username}", "bash" ]
                    
                                """).format( base_image=base_docker_image,
                                             commands=commands,
                                             username=image_username,
                                             groupname=image_groupname,
                                             image_code_dir=image_code_dir,
                                             maintainer=maintainer,
                                           ))
                    
                    base_dm.stream.write("Building Docker image...")
                    with base_dm.stream.DoneManager() as this_dm:
                        tags = [ "base",
                                 "base_latest",
                               ]

                        if now_tag:
                            tags.append("base_{}".format(now_tag))

                        command_line = 'docker build "{dir}" {tags}{squash}{force}' \
                                            .format( dir=base_image_dir,
                                                     tags=' '.join([ '--tag "{}:{}"'.format(docker_image_name, tag) for tag in tags ]),
                                                     squash='' if no_squash else " --squash",
                                                     force=" --no-cache" if force else '',
                                                   )
                        this_dm.result = Process.Execute(command_line, this_dm.stream)
                        if this_dm.result != 0:
                            return this_dm.result
                    
                if not no_activated_image:
                    # Create the activated image(s)
                    dm.stream.write("Creating activated image(s)...")
                    with dm.stream.DoneManager() as all_activated_dm:
                        for index, configuration in enumerate(repository_activation_configurations):
                            all_activated_dm.stream.write("Creating activated image{} ({} of {})...".format( '' if not configuration else " for the configuration '{}'".format(configuration),
                                                                                                             index + 1,
                                                                                                             len(repository_activation_configurations),
                                                                                                           ))
                            with all_activated_dm.stream.DoneManager(suffix='\n') as activated_dm:
                                this_activated_dir = os.path.join(activated_image_dir, configuration or "Default")
                                FileSystem.MakeDirs(this_activated_dir)

                                unique_id = str(uuid.uuid4())

                                temp_image_name = "{}_image".format(unique_id)
                                temp_container_name = "{}_container".format(unique_id)

                                # Activate the image so we can extract the changes
                                activated_dm.stream.write("Activating...")
                                with activated_dm.stream.DoneManager(suffix='\n') as this_dm:
                                    command_line = 'docker run -it --name "{container_name}" "{image_name}:base_latest" /sbin/my_init -- /sbin/setuser "{username}" bash -c "cd {image_code_dir} && . ./Activate.sh {configuration} && pushd {image_code_base}/Common/Environment && python -m RepositoryBootstrap.EnvironmentDiffs After /decorate' \
                                                        .format( container_name=temp_container_name,
                                                                 image_name=docker_image_name,
                                                                 configuration=configuration or '',
                                                                 username=image_username,
                                                                 image_code_dir=image_code_dir,
                                                                 image_code_base=image_code_base,
                                                               )

                                    sink = six.moves.StringIO()

                                    this_dm.result = Process.Execute(command_line, StreamDecorator([ sink, this_dm.stream, ]))
                                    if this_dm.result != 0:
                                        return this_dm.result

                                    sink = sink.getvalue()

                                activated_dm.stream.write("Extracting enviroment diffs...")
                                with activated_dm.stream.DoneManager():
                                    match = re.search( textwrap.dedent(
                                                            """\
                                                            //--//--//--//--//--//--//--//--//--//--//--//--//--//--//--//
                                                            (?P<content>.+?)
                                                            //--//--//--//--//--//--//--//--//--//--//--//--//--//--//--//
                                                            """),
                                                       sink,
                                                       re.DOTALL | re.MULTILINE,
                                                     )
                                    assert match, sink

                                    environment_diffs = json.loads(match.group("content"))

                                # ----------------------------------------------------------------------
                                def RemoveTempContainer():
                                    activated_dm.stream.write("Removing temp container...")
                                    with activated_dm.stream.DoneManager() as this_dm:
                                        this_dm.result, output = Process.Execute('docker rm "{}"'.format(temp_container_name))
                                        if this_dm.result != 0:
                                            this_dm.stream.write(output)

                                # ----------------------------------------------------------------------

                                with CallOnExit(RemoveTempContainer):
                                    # Commit the activated image
                                    activated_dm.stream.write("Committing container...")
                                    with activated_dm.stream.DoneManager() as this_dm:
                                        command_line = 'docker commit "{container_name}" "{image_name}"' \
                                                            .format( container_name=temp_container_name,
                                                                     image_name=temp_image_name,
                                                                   )

                                        this_dm.result, output = Process.Execute(command_line)
                                        if this_dm.result != 0:
                                            this_dm.stream.write(output)
                                            return this_dm.result

                                    # ----------------------------------------------------------------------
                                    def RemoveTempImage():
                                        if keep_temporary_image:
                                            return

                                        activated_dm.stream.write("Removing temp image...")
                                        with activated_dm.stream.DoneManager() as this_dm:
                                            this_dm.result, output = Process.Execute('docker rmi "{}"'.format(temp_image_name))
                                            if this_dm.result != 0:
                                                this_dm.stream.write(output)

                                    # ----------------------------------------------------------------------

                                    with CallOnExit(RemoveTempImage):
                                        # Create a new dockerfile. The temp image has all the harddrive changes
                                        # made during activation, but doesn't have the environment changes.
                                        activated_dm.stream.write("Creating dockerfile...")
                                        with activated_dm.stream.DoneManager() as this_dm:
                                            with open(os.path.join(this_activated_dir, "Dockerfile"), 'w') as f:
                                                f.write(textwrap.dedent(
                                                    """\
                                                    FROM {temp_image_name}

                                                    ENV {env}

                                                    # By default, run a bash prompt as the source code user
                                                    CMD [ "/sbin/my_init", "/sbin/setuser", "{username}", "bash" ]

                                                    LABEL maintainer="{maintainer}"

                                                    """).format( temp_image_name=temp_image_name,
                                                                 env='\\\n'.join([ '  {}={} '.format(k, v) for k, v in six.iteritems(environment_diffs) ]),
                                                                 image_code_dir=image_code_dir,
                                                                 maintainer=maintainer,
                                                                 username=image_username,
                                                               ))

                                        activated_dm.stream.write("Building Docker image...")
                                        with activated_dm.stream.DoneManager() as this_dm:
                                            tags = [ "latest",
                                                   ]

                                            if now_tag:
                                                tags.append(now_tag)

                                            if len(repository_activation_configurations) > 1:
                                                tags = [ "{}_{}".format(configuration, tag) for tag in tags ]
                                                tags.insert(0, configuration)

                                            command_line = 'docker build "{dir}" {tags}{squash}{force}' \
                                                                .format( dir=this_activated_dir,
                                                                         tags=' '.join([ '--tag "{}:{}"'.format(docker_image_name, tag) for tag in tags ]),
                                                                         squash='', # <squash is not supported here> '' if no_squash else " --squash",
                                                                         force=" --no-cache" if force else '',
                                                                       )

                                            this_dm.result = Process.Execute(command_line, this_dm.stream)
                                            if this_dm.result != 0:
                                                return this_dm.result
                                
                return dm.result
예제 #3
0
def Build(
    configuration,
    output_dir,
    release_build=False,
    prerelease_build_name=None,
    no_build_info=False,
    keep_temp_dir=False,
    cmake_generator=(
        None if os.getenv("DEVELOPMENT_ENVIRONMENT_REPOSITORY_CONFIGURATION") == "universal_linux" or os.getenv("DEVELOPMENT_ENVIRONMENT_CPP_USE_DEFAULT_CMAKE_GENERATOR") else "Ninja"
    ),
    output_stream=sys.stdout,
    verbose=False,
):
    """Builds the Featurizer Shared Library"""

    if release_build and prerelease_build_name:
        raise CommandLine.UsageException(
            "A prerelese build name cannot be provided with the 'release_build' flag",
        )

    with StreamDecorator(output_stream).DoneManager(
        line_prefix="",
        prefix="\nResults: ",
        suffix="\n",
    ) as dm:
        FileSystem.RemoveTree(output_dir)
        FileSystem.MakeDirs(output_dir)

        temp_directory = CurrentShell.CreateTempDirectory()

        # ----------------------------------------------------------------------
        def CleanupTempDir():
            if keep_temp_dir:
                dm.stream.write(
                    "\nCMake output has been written to '{}'.\n".format(temp_directory),
                )
                return

            FileSystem.RemoveTree(temp_directory)

        # ----------------------------------------------------------------------

        with CallOnExit(CleanupTempDir):
            prev_dir = os.getcwd()
            os.chdir(temp_directory)

            with CallOnExit(lambda: os.chdir(prev_dir)):
                if not release_build:
                    if prerelease_build_name is None:
                        # This value should compare as:
                        #   "manual" < "pipeline"
                        prerelease_build_name = "manual"

                    if not no_build_info:
                        now = datetime.datetime.now()

                        prerelease_build_name = "{prerelease_build_name}.{year}.{month}.{day}.{hour}.{minute}.{second}.{configuration}".format(
                            year=now.year,
                            month=now.month,
                            day=now.day,
                            hour=now.hour,
                            minute=now.minute,
                            second=now.second,
                            prerelease_build_name=prerelease_build_name,
                            configuration=configuration.lower(),
                        )

                activities = [
                    (
                        "Generating cmake Files",
                        'cmake {generator}-DCMAKE_BUILD_TYPE={configuration} {prerelease_build_name} "{this_dir}"'.format(
                            generator='-G "{}" '.format(
                                cmake_generator,
                            ) if cmake_generator else "",
                            temp_dir=temp_directory,
                            configuration=configuration,
                            this_dir=_script_dir,
                            prerelease_build_name="" if not prerelease_build_name else "-DPRODUCT_VERSION_PRERELEASE_INFO={}".format(
                                prerelease_build_name,
                            ),
                        ),
                    ),
                    ("Building", "cmake --build ."),
                ]

                if (
                    os.getenv("DEVELOPMENT_ENVIRONMENT_REPOSITORY_CONFIGURATION")
                    == "universal_linux"
                ):
                    activities.append(
                        (
                            "Verifying Universal Linux Binaries",
                            "libcheck libFeaturizers.so",
                        ),
                    )

                activities += [
                    ("Copying Binaries", _CopyBinaries),
                    ("Copying Data", _CopyData),
                    ("Copying Headers", _CopyHeaders),
                ]

                for index, (activity, command_line) in enumerate(activities):
                    dm.stream.write(
                        "{} ({} of {})...".format(activity, index + 1, len(activities)),
                    )
                    with dm.stream.DoneManager(
                        suffix="\n" if verbose else None,
                    ) as this_dm:
                        sink = six.moves.StringIO()

                        output_streams = [sink]

                        if verbose:
                            output_streams.append(
                                StreamDecorator(
                                    this_dm.stream,
                                    line_prefix="INFO: ",
                                ),
                            )

                        this_output_stream = StreamDecorator(output_streams)

                        if callable(command_line):
                            this_dm.result = command_line(
                                temp_directory,
                                output_dir,
                                this_output_stream,
                            )
                        else:
                            this_dm.result = Process.Execute(
                                command_line,
                                this_output_stream,
                            )

                        if this_dm.result != 0:
                            if not verbose:
                                this_dm.stream.write(sink.getvalue())

                            return this_dm.result

        return dm.result
    def ExtractCoverageInfo(
        self,
        coverage_filename,
        binary_filename,
        includes,
        excludes,
        output_stream,
    ):

        # This is a hack. The names extracted from the coverage files are mangled
        # while the names provided in includes and excludes are in the glob format.
        # Split the glob and then determine matches by checking to see if each component
        # is in the mangled name. There is a lot that could go wrong with this, but
        # hopefully it is good enough.

        # ----------------------------------------------------------------------
        def ProcessFilter(value):
            return [part for part in value.split("::") if part != "*"]

        # ----------------------------------------------------------------------
        def Matches(value, parts):
            for part in parts:
                if part not in value:
                    return False

            return True

        # ----------------------------------------------------------------------

        if excludes:
            excludes = [ProcessFilter(exclude) for exclude in excludes]
            excludes_func = lambda method_name: any(
                Matches(method_name, exclude) for exclude in excludes)
        else:
            excludes_func = lambda method_name: False

        if includes:
            includes = [ProcessFilter(include) for include in includes]
            includes_func = lambda method_name: any(
                Matches(method_name, include) for include in includes)
        else:
            includes_func = lambda method_name: True

        # ----------------------------------------------------------------------
        def ShouldInclude(method_name):
            return not excludes_func(method_name) and includes_func(
                method_name)

        # ----------------------------------------------------------------------

        # grcov will parse every file in the directory which isn't what we want here. Move the coverage
        # files for this binary to a temp dir, parse that dir, and then remove it.
        temp_directory = CurrentShell.CreateTempDirectory()

        with CallOnExit(lambda: FileSystem.RemoveTree(temp_directory)):
            # ----------------------------------------------------------------------
            def GetCoverageFilename(ext):
                dirname, basename = os.path.split(binary_filename)
                basename = os.path.splitext(basename)[0]

                for item in os.listdir(dirname):
                    fullpath = os.path.join(dirname, item)
                    if not os.path.isfile(fullpath):
                        continue

                    this_basename, this_ext = os.path.splitext(item)
                    if this_ext == ext and this_basename.startswith(basename):
                        return fullpath

                return None

            # ----------------------------------------------------------------------

            gcno_filename = GetCoverageFilename(".gcno")
            assert gcno_filename and os.path.isfile(gcno_filename), (
                binary_filename, gcno_filename)

            shutil.copyfile(
                gcno_filename,
                os.path.join(temp_directory, os.path.basename(gcno_filename)),
            )

            gcda_filename = GetCoverageFilename(".gcda")
            assert gcda_filename and os.path.isfile(gcda_filename), (
                binary_filename, gcda_filename)

            shutil.copyfile(
                gcda_filename,
                os.path.join(temp_directory, os.path.basename(gcda_filename)),
            )

            # Convert the content
            result = Process.Execute(
                '{} Lcov "/bin_dir={}" /type=ade'.format(
                    CurrentShell.CreateScriptName("ExtractCoverageInfo"),
                    temp_directory,
                ),
                output_stream,
            )

            if result != 0:
                return result

            # Note that the coverage files for all output was generated when coverage was stopped.
            # These coverage files are used to extract coverage percentages for display purposes.
            # Don't let the output name of the file fool you - these files are different from the
            # globally generated coverage file.
            coverage_filename = os.path.join(temp_directory, "lcov.info")
            assert os.path.isfile(coverage_filename), coverage_filename

            # Parse the file
            covered = 0
            not_covered = 0

            with open(coverage_filename) as f:
                for line in f.readlines():
                    content = json.loads(line)

                    if "method" not in content:
                        continue

                    content = content["method"]

                    if ("name" not in content or "total_covered" not in content
                            or "total_uncovered" not in content):
                        continue

                    if not ShouldInclude(content["name"]):
                        continue

                    covered += content["total_covered"]
                    not_covered += content["total_uncovered"]

            return covered, not_covered