| // Copyright 2017 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 static com.google.devtools.build.lib.rules.objc.ObjcProvider.MULTI_ARCH_LINKED_BINARIES; |
| import static com.google.devtools.build.lib.syntax.Type.STRING; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableListMultimap; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.FilesToRunProvider; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.analysis.stringtemplate.ExpansionException; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.rules.apple.AppleConfiguration; |
| import com.google.devtools.build.lib.rules.apple.ApplePlatform; |
| import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType; |
| import com.google.devtools.build.lib.rules.apple.AppleToolchain; |
| import com.google.devtools.build.lib.rules.apple.XcodeConfigProvider; |
| import com.google.devtools.build.lib.util.StringUtil; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| |
| /** Implementation for the "apple_stub_binary" rule. */ |
| public class AppleStubBinary implements RuleConfiguredTargetFactory { |
| |
| /** Provides substitutions for the make variables that can be used in an xcenv_based_path */ |
| private static class XcenvBasedPathVariableContext extends ConfigurationMakeVariableContext { |
| private final RuleContext ruleContext; |
| |
| /** The platform used to build $(PLATFORM_DIR). */ |
| private final ApplePlatform platform; |
| |
| /** The complete set of variables that may be used in paths. */ |
| public static final ImmutableList<String> DEFINED_VARS = |
| ImmutableList.of("$(SDKROOT)", "$(PLATFORM_DIR)"); |
| |
| public XcenvBasedPathVariableContext(RuleContext ruleContext, ApplePlatform platform) { |
| super(ruleContext, ruleContext.getRule().getPackage(), ruleContext.getConfiguration()); |
| this.ruleContext = ruleContext; |
| this.platform = platform; |
| } |
| |
| /** Throws an exception if the given path is not rooted at a defined Make variable. */ |
| public void validatePathRoot(String path) throws RuleErrorException { |
| for (String var : DEFINED_VARS) { |
| if (path.startsWith(var)) { |
| return; |
| } |
| } |
| |
| throw ruleContext.throwWithAttributeError( |
| AppleStubBinaryRule.XCENV_BASED_PATH_ATTR, |
| String.format( |
| PATH_INCORRECTLY_ROOTED_ERROR_FORMAT, |
| StringUtil.joinEnglishList(XcenvBasedPathVariableContext.DEFINED_VARS, "or"))); |
| } |
| |
| @Override |
| public String lookupVariable(String var) throws ExpansionException { |
| if (var.equals("SDKROOT")) { |
| return "__BAZEL_XCODE_SDKROOT__"; |
| } |
| if (var.equals("PLATFORM_DIR")) { |
| return AppleToolchain.platformDir(platform.getNameInPlist()); |
| } |
| // Intentionally do not call super, because we only want to allow these specific variables and |
| // discard any that might be inherited from toolchains and other contexts. |
| throw new ExpansionException("$(" + var + ") not defined"); |
| } |
| } |
| |
| @VisibleForTesting |
| public static final String PATH_INCORRECTLY_ROOTED_ERROR_FORMAT = |
| "The stub binary path must be rooted at %s"; |
| |
| @VisibleForTesting |
| public static final String PATH_NOT_NORMALIZED_ERROR = |
| "The stub binary path must be normalized (i.e., not contain \".\" or \"..\")"; |
| |
| @Override |
| public final ConfiguredTarget create(RuleContext ruleContext) |
| throws InterruptedException, RuleErrorException, ActionConflictException { |
| MultiArchSplitTransitionProvider.validateMinimumOs(ruleContext); |
| PlatformType platformType = MultiArchSplitTransitionProvider.getPlatformType(ruleContext); |
| |
| AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); |
| |
| ApplePlatform platform = appleConfiguration.getMultiArchPlatform(platformType); |
| ImmutableListMultimap<BuildConfiguration, ObjcProvider> configurationToDepsMap = |
| ruleContext.getPrerequisitesByConfiguration( |
| "deps", Mode.SPLIT, ObjcProvider.SKYLARK_CONSTRUCTOR); |
| |
| Artifact outputArtifact = |
| ObjcRuleClasses.intermediateArtifacts(ruleContext).combinedArchitectureBinary(); |
| |
| registerActions(ruleContext, platform, outputArtifact); |
| |
| NestedSetBuilder<Artifact> filesToBuild = |
| NestedSetBuilder.<Artifact>stableOrder().add(outputArtifact); |
| RuleConfiguredTargetBuilder targetBuilder = |
| ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build()); |
| |
| ObjcProvider.Builder objcProviderBuilder = |
| new ObjcProvider.Builder(ruleContext.getAnalysisEnvironment().getSkylarkSemantics()); |
| for (ObjcProvider depProvider : configurationToDepsMap.values()) { |
| objcProviderBuilder.addTransitiveAndPropagate(depProvider); |
| } |
| objcProviderBuilder.add(MULTI_ARCH_LINKED_BINARIES, outputArtifact); |
| |
| ObjcProvider objcProvider = objcProviderBuilder.build(); |
| // TODO(cparsons): Stop propagating ObjcProvider directly from this rule. |
| targetBuilder.addNativeDeclaredProvider(objcProvider); |
| |
| targetBuilder.addNativeDeclaredProvider( |
| new AppleExecutableBinaryInfo(outputArtifact, objcProvider)); |
| |
| return targetBuilder.build(); |
| } |
| |
| private static FilesToRunProvider xcrunwrapper(RuleContext ruleContext) { |
| return ruleContext.getExecutablePrerequisite("$xcrunwrapper", Mode.HOST); |
| } |
| |
| /** Registers the actions that copy the stub binary to the target's output. */ |
| private static void registerActions( |
| RuleContext ruleContext, |
| ApplePlatform platform, |
| Artifact outputBinary) |
| throws RuleErrorException { |
| CustomCommandLine copyCommandLine = |
| new CustomCommandLine.Builder() |
| .add("/bin/cp") |
| .addDynamicString(resolveXcenvBasedPath(ruleContext, platform)) |
| .addExecPaths(ImmutableList.of(outputBinary)) |
| .build(); |
| |
| ruleContext.registerAction( |
| ObjcRuleClasses.spawnAppleEnvActionBuilder( |
| XcodeConfigProvider.fromRuleContext(ruleContext), platform) |
| .setExecutable(xcrunwrapper(ruleContext)) |
| .addCommandLine(copyCommandLine) |
| .setMnemonic("CopyStubExecutable") |
| .addOutput(outputBinary) |
| .disableSandboxing() |
| .build(ruleContext)); |
| } |
| |
| /** |
| * Returns the value of the xcenv_based_path attribute, emitting an error if its format is |
| * invalid. |
| * |
| * @param ruleContext the rule context |
| * @param platform the Apple platform |
| * @return the path string, if it was valid |
| * @throws RuleErrorException If the path string was invalid because it was not rooted at one of |
| * the allowed environment variables or it was not normalized |
| */ |
| private static String resolveXcenvBasedPath(RuleContext ruleContext, ApplePlatform platform) |
| throws RuleErrorException { |
| String pathString = |
| ruleContext.attributes().get(AppleStubBinaryRule.XCENV_BASED_PATH_ATTR, STRING); |
| XcenvBasedPathVariableContext makeVariableContext = |
| new XcenvBasedPathVariableContext(ruleContext, platform); |
| |
| makeVariableContext.validatePathRoot(pathString); |
| |
| if (!PathFragment.isNormalized(pathString)) { |
| throw ruleContext.throwWithAttributeError( |
| AppleStubBinaryRule.XCENV_BASED_PATH_ATTR, PATH_NOT_NORMALIZED_ERROR); |
| } |
| |
| return ruleContext |
| .getExpander(makeVariableContext) |
| .expand(AppleStubBinaryRule.XCENV_BASED_PATH_ATTR, pathString); |
| } |
| } |