/* * Copyright 2015-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.rust; import static com.facebook.buck.rust.RustCompileUtils.ruleToCrateName; import com.facebook.buck.cxx.CxxDescriptionEnhancer; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.CxxPlatforms; import com.facebook.buck.cxx.Linker; import com.facebook.buck.cxx.NativeLinkable; import com.facebook.buck.cxx.NativeLinkableInput; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.FlavorDomain; import com.facebook.buck.model.Flavored; import com.facebook.buck.model.Pair; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.CommonDescriptionArg; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.HasDeclaredDeps; import com.facebook.buck.rules.HasSrcs; import com.facebook.buck.rules.HasTests; import com.facebook.buck.rules.ImplicitDepsInferringDescription; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.ToolProvider; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.facebook.buck.versions.VersionPropagator; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import java.util.List; import java.util.Map; import java.util.Optional; import org.immutables.value.Value; public class RustLibraryDescription implements Description<RustLibraryDescriptionArg>, ImplicitDepsInferringDescription<RustLibraryDescription.AbstractRustLibraryDescriptionArg>, Flavored, VersionPropagator<RustLibraryDescriptionArg> { private static final FlavorDomain<RustDescriptionEnhancer.Type> LIBRARY_TYPE = FlavorDomain.from("Rust Library Type", RustDescriptionEnhancer.Type.class); private final FlavorDomain<CxxPlatform> cxxPlatforms; private final RustBuckConfig rustBuckConfig; private final CxxPlatform defaultCxxPlatform; public RustLibraryDescription( RustBuckConfig rustBuckConfig, FlavorDomain<CxxPlatform> cxxPlatforms, CxxPlatform defaultCxxPlatform) { this.rustBuckConfig = rustBuckConfig; this.cxxPlatforms = cxxPlatforms; this.defaultCxxPlatform = defaultCxxPlatform; } @Override public Class<RustLibraryDescriptionArg> getConstructorArgType() { return RustLibraryDescriptionArg.class; } private RustCompileRule requireBuild( BuildRuleParams params, BuildRuleResolver resolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CxxPlatform cxxPlatform, RustBuckConfig rustBuckConfig, ImmutableList<String> extraFlags, ImmutableList<String> extraLinkerFlags, Iterable<com.facebook.buck.rules.args.Arg> linkerInputs, String crate, CrateType crateType, Linker.LinkableDepType depType, RustLibraryDescriptionArg args) throws NoSuchBuildTargetException { Pair<SourcePath, ImmutableSortedSet<SourcePath>> rootModuleAndSources = RustCompileUtils.getRootModuleAndSources( params.getBuildTarget(), resolver, pathResolver, ruleFinder, cxxPlatform, crate, args.getCrateRoot(), ImmutableSet.of("lib.rs"), args.getSrcs()); return RustCompileUtils.requireBuild( params, resolver, ruleFinder, cxxPlatform, rustBuckConfig, extraFlags, extraLinkerFlags, linkerInputs, crate, crateType, depType, rootModuleAndSources.getSecond(), rootModuleAndSources.getFirst()); } @Override public BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, RustLibraryDescriptionArg args) throws NoSuchBuildTargetException { final BuildTarget buildTarget = params.getBuildTarget(); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); ImmutableList.Builder<String> rustcArgs = ImmutableList.builder(); RustCompileUtils.addFeatures(buildTarget, args.getFeatures(), rustcArgs); rustcArgs.addAll(args.getRustcFlags()); rustcArgs.addAll(rustBuckConfig.getRustLibraryFlags()); String crate = args.getCrate().orElse(ruleToCrateName(params.getBuildTarget().getShortName())); // See if we're building a particular "type" and "platform" of this library, and if so, extract // them from the flavors attached to the build target. Optional<Map.Entry<Flavor, RustDescriptionEnhancer.Type>> type = LIBRARY_TYPE.getFlavorAndValue(buildTarget); Optional<CxxPlatform> cxxPlatform = cxxPlatforms.getValue(buildTarget); if (type.isPresent()) { // Uncommon case - someone explicitly invoked buck to build a specific flavor as the // direct target. CrateType crateType = type.get().getValue().getCrateType(); Linker.LinkableDepType depType; if (crateType.isDynamic()) { depType = Linker.LinkableDepType.SHARED; } else { if (crateType.isPic()) { depType = Linker.LinkableDepType.STATIC_PIC; } else { depType = Linker.LinkableDepType.STATIC; } } return requireBuild( params, resolver, pathResolver, ruleFinder, cxxPlatform.orElse(defaultCxxPlatform), rustBuckConfig, rustcArgs.build(), /* linkerArgs */ ImmutableList.of(), /* linkerInputs */ ImmutableList.of(), crate, crateType, depType, args); } // Common case - we're being invoked to satisfy some other rule's dependency. return new RustLibrary(params) { // RustLinkable @Override public com.facebook.buck.rules.args.Arg getLinkerArg( boolean direct, boolean isCheck, CxxPlatform cxxPlatform, Linker.LinkableDepType depType) { BuildRule rule; CrateType crateType; // Determine a crate type from preferred linkage and deptype. // NOTE: DYLIB requires upstream rustc bug #38795 to be fixed, because otherwise // the use of -rpath will break with flavored paths containing ','. if (isCheck) { crateType = CrateType.CHECK; } else { switch (args.getPreferredLinkage()) { case ANY: default: switch (depType) { case SHARED: crateType = CrateType.DYLIB; break; case STATIC_PIC: crateType = CrateType.RLIB_PIC; break; case STATIC: default: crateType = CrateType.RLIB; break; } break; case SHARED: crateType = CrateType.DYLIB; break; case STATIC: if (depType == Linker.LinkableDepType.STATIC) { crateType = CrateType.RLIB; } else { crateType = CrateType.RLIB_PIC; } break; } } try { rule = requireBuild( params, resolver, pathResolver, ruleFinder, cxxPlatform, rustBuckConfig, rustcArgs.build(), /* linkerArgs */ ImmutableList.of(), /* linkerInputs */ ImmutableList.of(), crate, crateType, depType, args); } catch (NoSuchBuildTargetException e) { throw new RuntimeException(e); } SourcePath rlib = rule.getSourcePathToOutput(); return new RustLibraryArg(pathResolver, crate, rlib, direct, params.getBuildDeps()); } @Override public Linkage getPreferredLinkage() { return args.getPreferredLinkage(); } @Override public ImmutableMap<String, SourcePath> getRustSharedLibraries(CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { ImmutableMap.Builder<String, SourcePath> libs = ImmutableMap.builder(); String sharedLibrarySoname = CrateType.DYLIB.filenameFor(crate, cxxPlatform); BuildRule sharedLibraryBuildRule = requireBuild( params, resolver, pathResolver, ruleFinder, cxxPlatform, rustBuckConfig, rustcArgs.build(), /* linkerArgs */ ImmutableList.of(), /* linkerInputs */ ImmutableList.of(), crate, CrateType.DYLIB, Linker.LinkableDepType.SHARED, args); libs.put(sharedLibrarySoname, sharedLibraryBuildRule.getSourcePathToOutput()); return libs.build(); } // NativeLinkable @Override public Iterable<? extends NativeLinkable> getNativeLinkableDeps() { return ImmutableList.of(); } @Override public Iterable<? extends NativeLinkable> getNativeLinkableExportedDeps() { return FluentIterable.from(getBuildDeps()).filter(NativeLinkable.class); } @Override public NativeLinkableInput getNativeLinkableInput( CxxPlatform cxxPlatform, Linker.LinkableDepType depType) throws NoSuchBuildTargetException { CrateType crateType; switch (depType) { case SHARED: crateType = CrateType.CDYLIB; break; case STATIC_PIC: crateType = CrateType.STATIC_PIC; break; case STATIC: default: crateType = CrateType.STATIC; break; } BuildRule rule = requireBuild( params, resolver, pathResolver, ruleFinder, cxxPlatform, rustBuckConfig, rustcArgs.build(), /* linkerArgs */ ImmutableList.of(), /* linkerInputs */ ImmutableList.of(), crate, crateType, depType, args); SourcePath lib = rule.getSourcePathToOutput(); SourcePathArg arg = SourcePathArg.of(lib); return NativeLinkableInput.builder().addArgs(arg).build(); } @Override public Linkage getPreferredLinkage(CxxPlatform cxxPlatform) { return args.getPreferredLinkage(); } @Override public ImmutableMap<String, SourcePath> getSharedLibraries(CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { ImmutableMap.Builder<String, SourcePath> libs = ImmutableMap.builder(); String sharedLibrarySoname = CxxDescriptionEnhancer.getSharedLibrarySoname( Optional.empty(), getBuildTarget(), cxxPlatform); BuildRule sharedLibraryBuildRule = requireBuild( params, resolver, pathResolver, ruleFinder, cxxPlatform, rustBuckConfig, rustcArgs.build(), /* linkerArgs */ ImmutableList.of(), /* linkerInputs */ ImmutableList.of(), crate, CrateType.CDYLIB, Linker.LinkableDepType.SHARED, args); libs.put(sharedLibrarySoname, sharedLibraryBuildRule.getSourcePathToOutput()); return libs.build(); } }; } @Override public void findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, CellPathResolver cellRoots, AbstractRustLibraryDescriptionArg constructorArg, ImmutableCollection.Builder<BuildTarget> extraDepsBuilder, ImmutableCollection.Builder<BuildTarget> targetGraphOnlyDepsBuilder) { extraDepsBuilder.addAll(rustBuckConfig.getRustCompiler().getParseTimeDeps()); extraDepsBuilder.addAll( rustBuckConfig.getLinker().map(ToolProvider::getParseTimeDeps).orElse(ImmutableList.of())); extraDepsBuilder.addAll(CxxPlatforms.getParseTimeDeps(cxxPlatforms.getValues())); } @Override public boolean hasFlavors(ImmutableSet<Flavor> flavors) { if (cxxPlatforms.containsAnyOf(flavors)) { return true; } for (RustDescriptionEnhancer.Type type : RustDescriptionEnhancer.Type.values()) { if (flavors.contains(type.getFlavor())) { return true; } } return false; } @Override public Optional<ImmutableSet<FlavorDomain<?>>> flavorDomains() { return Optional.of(ImmutableSet.of(cxxPlatforms, LIBRARY_TYPE)); } @BuckStyleImmutable @Value.Immutable interface AbstractRustLibraryDescriptionArg extends CommonDescriptionArg, HasDeclaredDeps, HasSrcs, HasTests { @Value.NaturalOrder ImmutableSortedSet<String> getFeatures(); List<String> getRustcFlags(); @Value.Default default NativeLinkable.Linkage getPreferredLinkage() { return NativeLinkable.Linkage.ANY; } Optional<String> getCrate(); Optional<SourcePath> getCrateRoot(); } }