| // Copyright 2014 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.lib.rules.objc; |
| |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.ArtifactRoot; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.AttributeMap; |
| import com.google.devtools.build.lib.packages.BuiltinRestriction; |
| import com.google.devtools.build.lib.packages.Type; |
| import com.google.devtools.build.lib.rules.cpp.CppModuleMap; |
| import com.google.devtools.build.lib.rules.cpp.CppModuleMap.UmbrellaHeaderStrategy; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import net.starlark.java.annot.StarlarkMethod; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.StarlarkThread; |
| import net.starlark.java.eval.StarlarkValue; |
| |
| /** Factory class for generating artifacts which are used as intermediate output. */ |
| // TODO(bazel-team): This should really be named DerivedArtifacts as it contains methods for |
| // final as well as intermediate artifacts. |
| public final class IntermediateArtifacts implements StarlarkValue { |
| static final String LINKMAP_SUFFIX = ".linkmap"; |
| |
| private final RuleContext ruleContext; |
| private final BuildConfigurationValue buildConfiguration; |
| private final String archiveFileNameSuffix; |
| private final UmbrellaHeaderStrategy umbrellaHeaderStrategy; |
| private final AlwaysLink alwaysLink; |
| |
| /** How to determine whether the archive is alwaysLink. */ |
| public enum AlwaysLink { |
| FROM_ATTRIBUTE, |
| TRUE, |
| FALSE |
| }; |
| |
| IntermediateArtifacts(RuleContext ruleContext) { |
| this( |
| ruleContext, |
| /* archiveFileNameSuffix= */ "", |
| ruleContext.getConfiguration(), |
| UmbrellaHeaderStrategy.DO_NOT_GENERATE, |
| AlwaysLink.FROM_ATTRIBUTE); |
| } |
| |
| IntermediateArtifacts(RuleContext ruleContext, BuildConfigurationValue buildConfiguration) { |
| this( |
| ruleContext, |
| /* archiveFileNameSuffix= */ "", |
| buildConfiguration, |
| UmbrellaHeaderStrategy.DO_NOT_GENERATE, |
| AlwaysLink.FROM_ATTRIBUTE); |
| } |
| |
| IntermediateArtifacts( |
| RuleContext ruleContext, |
| String archiveFileNameSuffix, |
| UmbrellaHeaderStrategy umbrellaHeaderStrategy, |
| AlwaysLink alwaysLink) { |
| this( |
| ruleContext, |
| archiveFileNameSuffix, |
| ruleContext.getConfiguration(), |
| umbrellaHeaderStrategy, |
| alwaysLink); |
| } |
| |
| IntermediateArtifacts( |
| RuleContext ruleContext, |
| String archiveFileNameSuffix, |
| BuildConfigurationValue buildConfiguration, |
| UmbrellaHeaderStrategy umbrellaHeaderStrategy, |
| AlwaysLink alwaysLink) { |
| this.ruleContext = ruleContext; |
| this.buildConfiguration = buildConfiguration; |
| this.archiveFileNameSuffix = Preconditions.checkNotNull(archiveFileNameSuffix); |
| this.umbrellaHeaderStrategy = umbrellaHeaderStrategy; |
| this.alwaysLink = alwaysLink; |
| } |
| |
| /** Returns the archive file name suffix. */ |
| @StarlarkMethod(name = "archive_file_name_suffix", documented = false, structField = true) |
| public String archiveFileNameSuffix() { |
| return archiveFileNameSuffix; |
| } |
| |
| /** |
| * Returns a derived artifact in the bin directory obtained by appending some extension to the end |
| * of the {@link PathFragment} corresponding to the owner {@link Label}. |
| */ |
| private Artifact appendExtension(String extension) { |
| PathFragment name = PathFragment.create(ruleContext.getLabel().getName()); |
| return scopedArtifact(name.replaceName(join(name.getBaseName(), extension))); |
| } |
| |
| /** |
| * Returns a derived artifact in the genfiles directory obtained by appending some extension to |
| * the end of the {@link PathFragment} corresponding to the owner {@link Label}. |
| */ |
| private Artifact appendExtensionInGenfiles(String extension) { |
| PathFragment name = PathFragment.create(ruleContext.getLabel().getName()); |
| return scopedArtifact( |
| name.replaceName(join(name.getBaseName(), extension)), /* inGenfiles= */ true); |
| } |
| |
| /** Lipo archive generated by combining one or more linked archives. */ |
| @StarlarkMethod(name = "combined_architecture_archive", documented = false, structField = true) |
| public Artifact combinedArchitectureArchive() { |
| return appendExtension("_lipo.a"); |
| } |
| |
| |
| private Artifact scopedArtifact(PathFragment scopeRelative, boolean inGenfiles) { |
| ArtifactRoot root = |
| inGenfiles |
| ? buildConfiguration.getGenfilesDirectory(ruleContext.getRule().getRepository()) |
| : buildConfiguration.getBinDirectory(ruleContext.getRule().getRepository()); |
| |
| // The path of this artifact will be RULE_PACKAGE/SCOPERELATIVE |
| return ruleContext.getPackageRelativeArtifact(scopeRelative, root); |
| } |
| |
| private Artifact scopedArtifact(PathFragment scopeRelative) { |
| return scopedArtifact(scopeRelative, /* inGenfiles = */ false); |
| } |
| |
| /** The archive file which contains all the compiled sources for a rule. */ |
| public Artifact archive() { |
| // The path will be {RULE_PACKAGE}/lib{RULEBASENAME}{.a,.lo,{SUFFIX}.a} |
| String basename = PathFragment.create(ruleContext.getLabel().getName()).getBaseName(); |
| String extension; |
| AttributeMap attributes = ruleContext.attributes(); |
| ObjcConfiguration objcConfiguration = buildConfiguration.getFragment(ObjcConfiguration.class); |
| |
| switch (alwaysLink) { |
| case FROM_ATTRIBUTE: |
| Preconditions.checkState(attributes.has("alwayslink", Type.BOOLEAN)); |
| if (attributes.isAttributeValueExplicitlySpecified("alwayslink") |
| ? attributes.get("alwayslink", Type.BOOLEAN) |
| : objcConfiguration.alwayslinkByDefault()) { |
| extension = ".lo"; |
| } else { |
| extension = ".a"; |
| } |
| break; |
| case TRUE: |
| extension = ".lo"; |
| break; |
| case FALSE: |
| default: |
| extension = ".a"; |
| break; |
| } |
| |
| return scopedArtifact( |
| PathFragment.create( |
| String.format("lib%s%s%s", basename, archiveFileNameSuffix, extension))); |
| } |
| |
| @StarlarkMethod(name = "archive", documented = false, useStarlarkThread = true) |
| public Artifact archiveForStarlark(StarlarkThread thread) throws EvalException { |
| BuiltinRestriction.failIfCalledOutsideBuiltins(thread); |
| return archive(); |
| } |
| |
| /** Representation for a specific architecture. */ |
| private Artifact architectureRepresentation(String arch, String suffix) { |
| return appendExtension(String.format("_%s%s", arch, suffix)); |
| } |
| |
| /** Linkmap representation */ |
| public Artifact linkmap() { |
| return appendExtension(LINKMAP_SUFFIX); |
| } |
| |
| /** Linkmap representation for a specific architecture. */ |
| public Artifact linkmap(String arch) { |
| return architectureRepresentation(arch, LINKMAP_SUFFIX); |
| } |
| |
| private String getModuleName() { |
| String moduleName; |
| if (ruleContext.attributes().isAttributeValueExplicitlySpecified("module_name")) { |
| moduleName = ruleContext.attributes().get("module_name", Type.STRING); |
| } else { |
| moduleName = |
| ruleContext |
| .getLabel() |
| .toString() |
| .replace("//", "") |
| .replace("@", "") |
| .replace("-", "_") |
| .replace("/", "_") |
| .replace(":", "_"); |
| } |
| return moduleName; |
| } |
| |
| /** {@link CppModuleMap} for swift. */ |
| @StarlarkMethod(name = "swift_module_map", documented = false, structField = true) |
| public CppModuleMap swiftModuleMap() { |
| String moduleName = getModuleName(); |
| Optional<Artifact> customModuleMap = CompilationSupport.getCustomModuleMap(ruleContext); |
| if (customModuleMap.isPresent()) { |
| return new CppModuleMap(customModuleMap.get(), moduleName); |
| } else if (umbrellaHeaderStrategy == UmbrellaHeaderStrategy.GENERATE) { |
| // To get Swift to pick up module maps, we need to name them "module.modulemap" and have their |
| // parent directory in the module map search paths. |
| return new CppModuleMap( |
| appendExtensionInGenfiles(".modulemaps/module.modulemap"), |
| appendExtensionInGenfiles(".modulemaps/umbrella.h"), |
| moduleName); |
| } else { |
| return new CppModuleMap( |
| appendExtensionInGenfiles(".modulemaps/module.modulemap"), moduleName); |
| } |
| } |
| |
| /** {@link CppModuleMap} for layering check and modules. */ |
| @StarlarkMethod(name = "internal_module_map", documented = false, structField = true) |
| public CppModuleMap internalModuleMap() { |
| return new CppModuleMap( |
| appendExtensionInGenfiles(".internal.cppmap"), ruleContext.getLabel().toString()); |
| } |
| |
| /** Returns baseName appeneded with extension. */ |
| private static String join(String baseName, String extension) { |
| return String.format("%s%s", baseName, extension); |
| } |
| } |