Ejemplo n.º 1
0
    def create_python_requirement_library(self, relpath, name, requirements):
        def make_requirement(req):
            return 'python_requirement("{}")'.format(req)

        self.create_file(relpath=self.build_path(relpath),
                         contents=dedent("""
    python_requirement_library(
      name='{name}',
      requirements=[
        {requirements}
      ]
    )
    """).format(name=name,
                requirements=','.join(map(make_requirement, requirements))))
        return self.target(Address(relpath, name).spec)
Ejemplo n.º 2
0
 def synthetic_target_extra_dependencies(self, target):
     deps = OrderedSet()
     if target.imported_jars:
         # We need to add in the proto imports jars.
         jars_address = Address(
             os.path.relpath(self.codegen_workdir(target), get_buildroot()),
             target.id + '-rjars')
         jars_target = self.context.add_new_target(
             jars_address,
             JarLibrary,
             jars=target.imported_jars,
             derived_from=target)
         deps.update([jars_target])
     deps.update(self.javadeps)
     return deps
Ejemplo n.º 3
0
    def test_bundle_filemap_dest_bypath(self):
        spec_path = 'src/java/org/archimedes/buoyancy'
        densities = self.create_file(
            os.path.join(spec_path, 'config/densities.xml'))
        unused = self.make_target(Address(spec_path, 'unused').spec, JvmBinary)

        app = self.make_target(
            spec_path,
            JvmApp,
            dependencies=[unused],
            bundles=[Bundle(spec_path, fileset='config/densities.xml')])

        self.assertEqual(1, len(app.bundles))
        # after one big refactor, ../../../../../ snuck into this path:
        self.assertEqual({densities: 'config/densities.xml'},
                         app.bundles[0].filemap)
Ejemplo n.º 4
0
    def create_sdk_jar_deps(self, binaries):
        """Create a JarLibrary target for every sdk in play.

    :param list binaries: A list of AndroidBinary targets.
    """
        # Prepare exactly N android jar targets where N is the number of SDKs in-play.
        for binary in binaries:
            sdk = binary.target_sdk
            if sdk not in self._jar_library_by_sdk:
                jar_url = 'file://{0}'.format(self.android_jar(binary))
                jar = JarDependency(org='com.google',
                                    name='android',
                                    rev=sdk,
                                    url=jar_url)
                address = Address(self.workdir, 'android-{0}.jar'.format(sdk))
                self._jar_library_by_sdk[sdk] = self.context.add_new_target(
                    address, JarLibrary, jars=[jar])
Ejemplo n.º 5
0
    def test_bundle_filemap_dest_relative(self):
        spec_path = 'src/java/org/archimedes/crown'
        five = self.create_file(os.path.join(spec_path,
                                             'gold/config/five.xml'))
        unused = self.make_target(Address(spec_path, 'unused').spec, JvmBinary)

        app = self.make_target(spec_path,
                               JvmApp,
                               dependencies=[unused],
                               bundles=[
                                   Bundle(spec_path,
                                          relative_to='gold',
                                          fileset='gold/config/five.xml')
                               ])

        self.assertEqual(1, len(app.bundles))
        self.assertEqual({five: 'config/five.xml'}, app.bundles[0].filemap)
Ejemplo n.º 6
0
    def create_resource_target(self, target, archive, manifest, resource_dir):
        """Create an AndroidResources target.

    :param AndroidLibrary target: AndroidLibrary that the new AndroidResources target derives from.
    :param string archive: Archive name as fetched by ivy, e.g. 'org.pantsbuild.example-1.0.aar'.
    :param string resource_dir: Full path of the res directory contained within aar files.
    :return: new_target.
    :rtype::class:`pants.backend.android.targets.AndroidResources`
    """

        address = Address(self.workdir, '{}-resources'.format(archive))
        new_target = self.context.add_new_target(address,
                                                 AndroidResources,
                                                 manifest=manifest,
                                                 resource_dir=resource_dir,
                                                 derived_from=target)
        return new_target
Ejemplo n.º 7
0
    def create_android_library_target(self, target, archive,
                                      unpacked_aar_location):
        """Create an AndroidLibrary target.

    The aar files are unpacked and the contents used to create a new AndroidLibrary target.
    :param AndroidLibrary target: AndroidLibrary that the new AndroidLibrary target derives from.
    :param string archive: An archive name as fetched by ivy, e.g. 'org.pantsbuild.example-1.0.aar'.
    :param string unpacked_aar_location: Full path of dir holding contents of an unpacked aar file.
    :return: new_target
    :rtype::class:`pants.backend.android.targets.AndroidLibrary`
    """
        # The following three elements of an aar file have names mandated by the aar spec:
        #   http://tools.android.com/tech-docs/new-build-system/aar-format
        # They are said to be mandatory although in practice that assumption only holds for manifest.
        manifest = os.path.join(unpacked_aar_location, 'AndroidManifest.xml')
        jar_file = os.path.join(unpacked_aar_location, 'classes.jar')
        resource_dir = os.path.join(unpacked_aar_location, 'res')

        # Sanity check to make sure all .aar files we expect to be unpacked are actually unpacked.
        if not os.path.isfile(manifest):
            raise self.MissingElementException(
                "An AndroidManifest.xml is expected in every unpacked "
                ".aar file but none was found in the {} archive "
                "for the {} target".format(archive, target))

        # Depending on the contents of the unpacked aar file, create the dependencies.
        deps = []
        if os.path.isdir(resource_dir):
            deps.append(
                self.create_resource_target(target, archive, manifest,
                                            resource_dir))
        if os.path.isfile(jar_file):
            deps.append(
                self.create_classes_jar_target(target, archive, jar_file))

        address = Address(self.workdir, '{}-android_library'.format(archive))
        new_target = self.context.add_new_target(
            address,
            AndroidLibrary,
            manifest=manifest,
            include_patterns=target.include_patterns,
            exclude_patterns=target.exclude_patterns,
            dependencies=deps,
            derived_from=target)
        return new_target
Ejemplo n.º 8
0
  def create_resource_target(self, target, coordinate, manifest, resource_dir):
    """Create an AndroidResources target.

    :param target: AndroidLibrary that the new AndroidResources target derives from.
    :type target: :class:`pants.backend.android.targets.android_library.AndroidLibrary`
    :param coordinate: Archive coordinate fetched by ivy, e.g. 'org.pantsbuild:example::1.0:aar'.
    :type coordinate: :class:`pants.backend.jvm.jar_dependency_utils.M2Coordinate`
    :param string manifest: The path of 'AndroidManifest.xml'
    :param string resource_dir: Full path of the res directory contained within aar files.
    :return: A new android resources target.
    :rtype::class:`pants.backend.android.targets.AndroidResources`
    """

    address = Address(self.workdir, '{}-resources'.format(coordinate.artifact_filename))
    new_target = self.context.add_new_target(address, AndroidResources,
                                             manifest=manifest, resource_dir=resource_dir,
                                             derived_from=target)
    return new_target
Ejemplo n.º 9
0
    def test_bundle_filemap_dest_remap(self):
        spec_path = 'src/java/org/archimedes/crown'
        one = self.create_file(os.path.join(spec_path, 'config/one.xml'))
        unused = self.make_target(Address(spec_path, 'unused').spec, JvmBinary)

        mapper = DirectoryReMapper(os.path.join(spec_path, 'config'),
                                   'gold/config')
        app = self.make_target(spec_path,
                               JvmApp,
                               dependencies=[unused],
                               bundles=[
                                   Bundle(spec_path,
                                          mapper=mapper,
                                          fileset='config/one.xml')
                               ])

        self.assertEqual(1, len(app.bundles))
        self.assertEqual({one: 'gold/config/one.xml'}, app.bundles[0].filemap)
Ejemplo n.º 10
0
    def test_bundle_filemap_dest_byglobs(self):
        spec_path = 'src/java/org/archimedes/tub'
        one = self.create_file(os.path.join(spec_path, 'config/one.xml'))
        two = self.create_file(os.path.join(spec_path, 'config/two.xml'))
        unused = self.make_target(Address(spec_path, 'unused').spec, JvmBinary)

        globs = Globs(spec_path)
        app = self.make_target(
            spec_path,
            JvmApp,
            dependencies=[unused],
            bundles=[Bundle(spec_path, fileset=globs('config/*.xml'))])

        self.assertEqual(1, len(app.bundles))
        self.assertEqual({
            one: 'config/one.xml',
            two: 'config/two.xml'
        }, app.bundles[0].filemap)
Ejemplo n.º 11
0
    def spec_to_address(self, spec, relative_to=''):
        """A helper method for mapping a spec to the correct address.

    :param string spec: A spec to lookup in the map.
    :param string relative_to: Path the spec might be relative to
    :raises :class:`pants.base.address_lookup_error.AddressLookupError` If the BUILD file cannot be
            found in the path specified by the spec.
    :returns: A new Address instance.
    :rtype: :class:`pants.base.address.Address`
    """
        spec_path, name = parse_spec(spec, relative_to=relative_to)
        try:
            self.from_cache(self.root_dir, spec_path)
        except BuildFile.BuildFileError as e:
            raise self.InvalidBuildFileReference(
                '{message}\n  when translating spec {spec}'.format(message=e,
                                                                   spec=spec))
        return Address(spec_path, name)
Ejemplo n.º 12
0
  def create_classes_jar_target(self, target, coordinate, jar_file):
    """Create a JarLibrary target containing the jar_file as a JarDependency.

    :param target: The new JarLibrary will be derived from this AndroidLibrary.
    :type target: :class:`pants.backend.android.targets.android_library.AndroidLibrary`
    :param coordinate: Archive coordinate fetched by ivy, e.g. 'org.pantsbuild:example::1.0:aar'.
    :type coordinate: :class:`pants.backend.jvm.jar_dependency_utils.M2Coordinate`
    :param string jar_file: Full path of the classes.jar contained within unpacked aar files.
    :returns: A new jar library target.
    :rtype: :class:`pants.backend.jvm.targets.jar_library.JarLibrary`
    """
    # TODO(mateor) add another JarDependency for every jar under 'libs'.
    jar_url = 'file://{0}'.format(jar_file)
    jar_dep = JarDependency(org=target.id, name=coordinate.artifact_filename, rev=coordinate.rev,
                            url=jar_url)
    address = Address(self.workdir, '{}-classes.jar'.format(coordinate.artifact_filename))
    new_target = self.context.add_new_target(address, JarLibrary, jars=[jar_dep],
                                             derived_from=target)
    return new_target
Ejemplo n.º 13
0
    def create_target(self, binary, gentarget):
        """Create a JavaLibrary target for the R.java files created by the aapt tool.

    :param AndroidBinary binary: AndroidBinary target whose target_sdk is used.
    :param AndroidTarget gentarget: AndroidBinary or Library that owns the processed resources.

    :returns new_target: Synthetic target for the R.java output of the aapt tool.
    :rtype::class:`pants.backend.jvm.targets.java_library.JavaLibrary`
    """
        spec_path = os.path.join(
            os.path.relpath(self.aapt_out(binary), get_buildroot()))
        address = Address(spec_path=spec_path, target_name=gentarget.id)
        deps = [self._jar_library_by_sdk[binary.target_sdk]]
        new_target = self.context.add_new_target(
            address,
            JavaLibrary,
            derived_from=gentarget,
            sources=[self._relative_genfile(gentarget)],
            dependencies=deps)
        return new_target
Ejemplo n.º 14
0
 def create_python_binary(self,
                          relpath,
                          name,
                          entry_point,
                          dependencies=(),
                          provides=None):
     self.create_file(relpath=self.build_path(relpath),
                      contents=dedent("""
 python_binary(
   name='{name}',
   entry_point='{entry_point}',
   dependencies=[
     {dependencies}
   ],
   {provides_clause}
 )
 """).format(name=name,
             entry_point=entry_point,
             dependencies=','.join(map(repr, dependencies)),
             provides_clause='provides={0},'.format(provides)
             if provides else ''))
     return self.target(Address(relpath, name).spec)
Ejemplo n.º 15
0
    def test_bundle_add(self):
        spec_path = 'src/java/org/archimedes/volume'
        stone_dense = self.create_file(
            os.path.join(spec_path, 'config/stone/dense.xml'))
        metal_dense = self.create_file(
            os.path.join(spec_path, 'config/metal/dense.xml'))
        unused = self.make_target(Address(spec_path, 'unused').spec, JvmBinary)

        bundle = Bundle(
            spec_path,
            relative_to='config',
            fileset=['config/stone/dense.xml', 'config/metal/dense.xml'])
        app = self.make_target(spec_path,
                               JvmApp,
                               dependencies=[unused],
                               bundles=[bundle])

        self.assertEqual(1, len(app.bundles))
        self.assertEqual(
            {
                stone_dense: 'stone/dense.xml',
                metal_dense: 'metal/dense.xml'
            }, app.bundles[0].filemap)
Ejemplo n.º 16
0
    def create_classes_jar_target(self, target, archive, jar_file):
        """Create a JarLibrary target containing the jar_file as a JarDependency.

    :param AndroidLibrary target: The new JarLibrary will be derived from this AndroidLibrary .
    :param string archive: Archive name as fetched by ivy, e.g. 'org.pantsbuild.example-1.0.aar'.
    :param string jar_file: Full path of the classes.jar contained within unpacked aar files.
    :return: new_target.
    :rtype::class:`pants.backend.jvm.targets.java_library.JarLibrary`
    """
        # TODO(mateor) add another JarDependency for every jar under 'libs'.

        # Try to parse revision number. This is just to satisfy the spec, the rev is part of 'archive'.
        archive_version = os.path.splitext(archive)[0].rpartition('-')[-1]
        jar_url = 'file://{0}'.format(jar_file)
        jar_dep = JarDependency(org=target.id,
                                name=archive,
                                rev=archive_version,
                                url=jar_url)
        address = Address(self.workdir, '{}-classes.jar'.format(archive))
        new_target = self.context.add_new_target(address,
                                                 JarLibrary,
                                                 jars=[jar_dep],
                                                 derived_from=target)
        return new_target
Ejemplo n.º 17
0
 def test_equivalence(self):
   self.assertEqual(Address('a/b', 'c'), Address('a/b', 'c'))
   self.assertEqual(Address('a/b', 'c'), Address.parse('a/b:c'))
   self.assertEqual(Address.parse('a/b:c'), Address.parse('a/b:c'))
Ejemplo n.º 18
0
    def execute(self):
        targets = self.codegen_targets()
        with self.invalidated(
                targets,
                invalidate_dependents=True,
                fingerprint_strategy=self.get_fingerprint_strategy(
                )) as invalidation_check:
            invalid_targets = OrderedSet()
            for vts in invalidation_check.invalid_vts:
                invalid_targets.update(vts.targets)
            self.codegen_strategy.execute_codegen(invalid_targets)

            invalid_vts_by_target = dict([
                (vt.target, vt) for vt in invalidation_check.invalid_vts
            ])
            vts_artifactfiles_pairs = []

            for target in targets:
                target_workdir = self.codegen_workdir(target)
                synthetic_name = target.id
                sources_rel_path = os.path.relpath(target_workdir,
                                                   get_buildroot())
                synthetic_address = Address(sources_rel_path, synthetic_name)
                raw_generated_sources = list(
                    self.codegen_strategy.find_sources(target))
                # Make the sources robust regardless of whether subclasses return relative paths, or
                # absolute paths that are subclasses of the workdir.
                generated_sources = [
                    src if src.startswith(target_workdir) else os.path.join(
                        target_workdir, src) for src in raw_generated_sources
                ]
                relative_generated_sources = [
                    os.path.relpath(src, target_workdir)
                    for src in generated_sources
                ]

                self.target = self.context.add_new_target(
                    address=synthetic_address,
                    target_type=self.synthetic_target_type,
                    dependencies=self.synthetic_target_extra_dependencies(
                        target),
                    sources=relative_generated_sources,
                    derived_from=target,

                    # TODO(John Sirois): This assumes - currently, a JvmTarget or PythonTarget which both
                    # happen to have this attribute for carrying publish metadata but share no interface
                    # that defines this canonical property.  Lift up an interface and check for it or else
                    # add a way for SimpleCodeGen subclasses to specify extra attribute names that should be
                    # copied over from the target to its derived target.
                    provides=target.provides,
                )
                synthetic_target = self.target

                build_graph = self.context.build_graph

                # NB(pl): This bypasses the convenience function (Target.inject_dependency) in order
                # to improve performance.  Note that we can walk the transitive dependee subgraph once
                # for transitive invalidation rather than walking a smaller subgraph for every single
                # dependency injected.
                for dependent_address in build_graph.dependents_of(
                        target.address):
                    build_graph.inject_dependency(
                        dependent=dependent_address,
                        dependency=synthetic_target.address,
                    )
                # NB(pl): See the above comment.  The same note applies.
                for concrete_dependency_address in build_graph.dependencies_of(
                        target.address):
                    build_graph.inject_dependency(
                        dependent=synthetic_target.address,
                        dependency=concrete_dependency_address,
                    )
                build_graph.walk_transitive_dependee_graph(
                    build_graph.dependencies_of(target.address),
                    work=lambda t: t.mark_transitive_invalidation_hash_dirty(),
                )

                if target in self.context.target_roots:
                    self.context.target_roots.append(synthetic_target)
                if target in invalid_vts_by_target:
                    vts_artifactfiles_pairs.append(
                        (invalid_vts_by_target[target], generated_sources))

            if self.artifact_cache_writes_enabled():
                self.update_artifact_cache(vts_artifactfiles_pairs)
Ejemplo n.º 19
0
    def execute(self):
        targets = self.codegen_targets()
        build_graph = self.context.build_graph
        with self.invalidated(
                targets, invalidate_dependents=True) as invalidation_check:
            for vts in invalidation_check.invalid_vts_partitioned:
                invalid_targets = vts.targets
                self.execute_codegen(invalid_targets)

            invalid_vts_by_target = dict([
                (vt.target, vt) for vt in invalidation_check.invalid_vts
            ])
            vts_artifactfiles_pairs = defaultdict(list)

            for target in targets:
                java_synthetic_name = '{0}-{1}'.format(target.id, 'java')
                java_sources_rel_path = os.path.relpath(
                    self.namespace_out, get_buildroot())
                java_synthetic_address = Address(java_sources_rel_path,
                                                 java_synthetic_name)
                java_generated_sources = [
                    os.path.join(
                        os.path.dirname(source),
                        'java_{0}.java'.format(os.path.basename(source)))
                    for source in self.sources_generated_by_target(target)
                ]
                java_relative_generated_sources = [
                    os.path.relpath(src, self.namespace_out)
                    for src in java_generated_sources
                ]

                # We can't use context.add_new_target because it now does fancy management
                # of synthetic target / target root interaction that breaks us here.
                java_target_base = os.path.join(
                    get_buildroot(), java_synthetic_address.spec_path)
                if not os.path.exists(java_target_base):
                    os.makedirs(java_target_base)
                SourceRoot.register(java_synthetic_address.spec_path)
                build_graph.inject_synthetic_target(
                    address=java_synthetic_address,
                    target_type=JavaLibrary,
                    dependencies=[
                        dep.address
                        for dep in self.synthetic_target_extra_dependencies
                    ],
                    derived_from=target,
                    sources=java_relative_generated_sources,
                )
                java_synthetic_target = build_graph.get_target(
                    java_synthetic_address)

                # NOTE(pl): This bypasses the convenience function (Target.inject_dependency) in order
                # to improve performance.  Note that we can walk the transitive dependee subgraph once
                # for transitive invalidation rather than walking a smaller subgraph for every single
                # dependency injected.  This walk is done below, after the scala synthetic target is
                # injected.
                for concrete_dependency_address in build_graph.dependencies_of(
                        target.address):
                    build_graph.inject_dependency(
                        dependent=java_synthetic_target.address,
                        dependency=concrete_dependency_address,
                    )

                if target in invalid_vts_by_target:
                    vts_artifactfiles_pairs[invalid_vts_by_target[
                        target]].extend(java_generated_sources)

                synthetic_name = '{0}-{1}'.format(target.id, 'scala')
                sources_rel_path = os.path.relpath(self.namespace_out,
                                                   get_buildroot())
                synthetic_address = Address(sources_rel_path, synthetic_name)
                generated_sources = [
                    '{0}.{1}'.format(source, 'scala')
                    for source in self.sources_generated_by_target(target)
                ]
                relative_generated_sources = [
                    os.path.relpath(src, self.namespace_out)
                    for src in generated_sources
                ]
                synthetic_target = self.context.add_new_target(
                    address=synthetic_address,
                    target_type=ScalaLibrary,
                    dependencies=self.synthetic_target_extra_dependencies,
                    sources=relative_generated_sources,
                    derived_from=target,
                    java_sources=[java_synthetic_target.address.spec],
                )

                # NOTE(pl): This bypasses the convenience function (Target.inject_dependency) in order
                # to improve performance.  Note that we can walk the transitive dependee subgraph once
                # for transitive invalidation rather than walking a smaller subgraph for every single
                # dependency injected.  This walk also covers the invalidation for the java synthetic
                # target above.
                for dependent_address in build_graph.dependents_of(
                        target.address):
                    build_graph.inject_dependency(
                        dependent=dependent_address,
                        dependency=synthetic_target.address)
                # NOTE(pl): See the above comment.  The same note applies.
                for concrete_dependency_address in build_graph.dependencies_of(
                        target.address):
                    build_graph.inject_dependency(
                        dependent=synthetic_target.address,
                        dependency=concrete_dependency_address,
                    )
                build_graph.walk_transitive_dependee_graph(
                    [target.address],
                    work=lambda t: t.mark_transitive_invalidation_hash_dirty(),
                )

                if target in self.context.target_roots:
                    self.context.target_roots.append(synthetic_target)
                if target in invalid_vts_by_target:
                    vts_artifactfiles_pairs[invalid_vts_by_target[
                        target]].extend(generated_sources)

            if self.artifact_cache_writes_enabled():
                self.update_artifact_cache(vts_artifactfiles_pairs.items())
Ejemplo n.º 20
0
 def get_antlr_syn_target(self, task):
     target = self.get_antlr_target()
     target_workdir = task.codegen_workdir(target)
     syn_address = Address(os.path.relpath(target_workdir, get_buildroot()),
                           target.id)
     return task.context.build_graph.get_target(syn_address)
Ejemplo n.º 21
0
    def _parse_spec(self, spec, fail_fast=False):
        def normalize_spec_path(path):
            is_abs = not path.startswith('//') and os.path.isabs(path)
            if is_abs:
                path = os.path.realpath(path)
                if os.path.commonprefix([self._root_dir, path
                                         ]) != self._root_dir:
                    raise self.BadSpecError(
                        'Absolute address path {0} does not share build root {1}'
                        .format(path, self._root_dir))
            else:
                if path.startswith('//'):
                    path = path[2:]
                path = os.path.join(self._root_dir, path)

            normalized = os.path.relpath(path, self._root_dir)
            if normalized == '.':
                normalized = ''
            return normalized

        errored_out = []

        if spec.endswith('::'):
            addresses = set()
            spec_path = spec[:-len('::')]
            spec_dir = normalize_spec_path(spec_path)
            try:
                build_files = self._address_mapper.scan_buildfiles(
                    self._root_dir,
                    spec_dir,
                    spec_excludes=self._spec_excludes)
            except (BuildFile.BuildFileError, AddressLookupError) as e:
                raise self.BadSpecError(e)

            for build_file in build_files:
                try:
                    # This attempts to filter out broken BUILD files before we parse them.
                    if self._not_excluded_spec(build_file.spec_path):
                        addresses.update(
                            self._address_mapper.addresses_in_spec_path(
                                build_file.spec_path))
                except (BuildFile.BuildFileError, AddressLookupError) as e:
                    if fail_fast:
                        raise self.BadSpecError(e)
                    errored_out.append('--------------------')
                    errored_out.append(traceback.format_exc())
                    errored_out.append('Exception message: {0}'.format(e))

            if errored_out:
                error_msg = '\n'.join(
                    errored_out +
                    ["Invalid BUILD files for [{0}]".format(spec)])
                raise self.BadSpecError(error_msg)
            return addresses

        elif spec.endswith(':'):
            spec_path = spec[:-len(':')]
            spec_dir = normalize_spec_path(spec_path)
            try:
                return set(
                    self._address_mapper.addresses_in_spec_path(spec_dir))
            except AddressLookupError as e:
                raise self.BadSpecError(e)
        else:
            spec_parts = spec.rsplit(':', 1)
            spec_parts[0] = normalize_spec_path(spec_parts[0])
            spec_path, target_name = parse_spec(':'.join(spec_parts))
            try:
                self._address_mapper.from_cache(self._root_dir, spec_path)
            except BuildFile.BuildFileError as e:
                raise self.BadSpecError(e)
            return {Address(spec_path, target_name)}
Ejemplo n.º 22
0
    def _transitive_download_remote_libs(self,
                                         go_remote_libs,
                                         all_known_addresses=None):
        """Recursively attempt to resolve / download all remote transitive deps of go_remote_libs.

    Returns a dict<GoRemoteLibrary, set<tuple<str, Address>>>, which maps a go remote library to a
    set of unresolved remote dependencies, each dependency expressed as a tuple containing the
    the import path of the dependency and the expected target address. If all transitive
    dependencies were successfully resolved, returns an empty dict.

    Downloads as many invalidated transitive dependencies as possible, and returns as many
    undeclared dependencies as possible. However, because the dependencies of a remote library
    can only be determined _after_ it has been downloaded, a transitive dependency of an undeclared
    remote library will never be detected.

    Because go_remote_libraries do not declare dependencies (rather, they are inferred), injects
    all successfully resolved transitive dependencies into the build graph.
    """
        if not go_remote_libs:
            return {}

        all_known_addresses = all_known_addresses or set()
        all_known_addresses.update(lib.address for lib in go_remote_libs)

        resolved_remote_libs = set()
        undeclared_deps = defaultdict(set)
        go_remote_lib_src = self.context.products.get_data('go_remote_lib_src')

        with self.invalidated(go_remote_libs) as invalidation_check:
            for vt in invalidation_check.all_vts:
                go_remote_lib = vt.target
                gopath = vt.results_dir
                fetcher = self._get_fetcher(go_remote_lib.import_path)

                if not vt.valid:
                    root = fetcher.root(go_remote_lib.import_path)
                    fetch_dir = os.path.join(self.workdir, 'fetches')
                    root_dir = os.path.join(fetch_dir, root)

                    # Only fetch each remote root once.
                    if not os.path.exists(root_dir):
                        with temporary_dir() as tmp_fetch_root:
                            fetcher.fetch(go_remote_lib.import_path,
                                          dest=tmp_fetch_root,
                                          rev=go_remote_lib.rev)
                            safe_mkdir(root_dir)
                            for path in os.listdir(tmp_fetch_root):
                                shutil.move(os.path.join(tmp_fetch_root, path),
                                            os.path.join(root_dir, path))

                    # TODO(John Sirois): Circle back and get get rid of this symlink tree.
                    # GoWorkspaceTask will further symlink a single package from the tree below into a
                    # target's workspace when it could just be linking from the fetch_dir.  The only thing
                    # standing in the way is a determination of what we want to artifact cache.  If we don't
                    # want to cache fetched zips, linking straight from the fetch_dir works simply.  Otherwise
                    # thought needs to be applied to using the artifact cache directly or synthesizing a
                    # canonical owner target for the fetched files that 'child' targets (subpackages) can
                    # depend on and share the fetch from.
                    dest_dir = os.path.join(gopath, 'src', root)
                    # We may have been `invalidate`d and not `clean-all`ed so we need a new empty symlink
                    # chroot to avoid collision; thus `clean=True`.
                    safe_mkdir(dest_dir, clean=True)
                    for path in os.listdir(root_dir):
                        os.symlink(os.path.join(root_dir, path),
                                   os.path.join(dest_dir, path))

                # Map the fetched remote sources.
                pkg = go_remote_lib.import_path
                go_remote_lib_src[go_remote_lib] = os.path.join(
                    gopath, 'src', pkg)

                for remote_import_path in self._get_remote_import_paths(
                        pkg, gopath=gopath):
                    fetcher = self._get_fetcher(remote_import_path)
                    remote_root = fetcher.root(remote_import_path)
                    spec_path = os.path.join(go_remote_lib.target_base,
                                             remote_root)

                    package_path = GoRemoteLibrary.remote_package_path(
                        remote_root, remote_import_path)
                    target_name = package_path or os.path.basename(remote_root)

                    address = Address(spec_path, target_name)
                    if address not in all_known_addresses:
                        try:
                            # If we've already resolved a package from this remote root, its ok to define an
                            # implicit synthetic remote target for all other packages in the same remote root.
                            implicit_ok = any(spec_path == a.spec_path
                                              for a in all_known_addresses)

                            remote_lib = self._resolve(go_remote_lib, address,
                                                       package_path,
                                                       implicit_ok)
                            resolved_remote_libs.add(remote_lib)
                            all_known_addresses.add(address)
                        except self.UndeclaredRemoteLibError as e:
                            undeclared_deps[go_remote_lib].add(
                                (remote_import_path, e.address))
                    self.context.build_graph.inject_dependency(
                        go_remote_lib.address, address)

    # Recurse after the invalidated block, so the libraries we downloaded are now "valid"
    # and thus we don't try to download a library twice.
        trans_undeclared_deps = self._transitive_download_remote_libs(
            resolved_remote_libs, all_known_addresses)
        undeclared_deps.update(trans_undeclared_deps)

        return undeclared_deps
Ejemplo n.º 23
0
 def address(name):
   return Address(spec_path='examples/graph_test', target_name=name)