Skip to content

Commit

Permalink
Introduce apple_binary, which links together one or more c-family lib…
Browse files Browse the repository at this point in the history
…raries and produces a potentially multi-architecture binary, controlled by the --ios_multi_cpus flag

--
MOS_MIGRATED_REVID=120874805
  • Loading branch information
c-parsons authored and meteorcloudy committed Apr 27, 2016
1 parent 140634e commit b5a76c5
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import com.google.devtools.build.lib.rules.java.JavaToolchainRule;
import com.google.devtools.build.lib.rules.java.JvmConfigurationLoader;
import com.google.devtools.build.lib.rules.java.ProguardLibraryRule;
import com.google.devtools.build.lib.rules.objc.AppleBinaryRule;
import com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon;
import com.google.devtools.build.lib.rules.objc.AppleWatch1ExtensionRule;
import com.google.devtools.build.lib.rules.objc.AppleWatchExtensionBinaryRule;
Expand Down Expand Up @@ -361,6 +362,7 @@ public static void setup(ConfiguredRuleClassProvider.Builder builder) {

builder.addRuleDefinition(new IosTestRule());
builder.addRuleDefinition(new IosDeviceRule());
builder.addRuleDefinition(new AppleBinaryRule());
builder.addRuleDefinition(new ObjcBinaryRule());
builder.addRuleDefinition(new ExperimentalObjcLibraryRule());
builder.addRuleDefinition(new ObjcBundleRule());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Copyright 2016 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.IMPORTED_LIBRARY;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.rules.apple.AppleConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider;
import com.google.devtools.build.lib.rules.cpp.CppCompilationContext;
import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs;
import com.google.devtools.build.lib.rules.objc.ObjcCommon.CompilationAttributes;
import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes;

import java.util.List;
import java.util.Set;

/**
* Implementation for the "apple_binary" rule.
*/
public class AppleBinary implements RuleConfiguredTargetFactory {
@VisibleForTesting
static final String REQUIRES_AT_LEAST_ONE_LIBRARY_OR_SOURCE_FILE =
"At least one library dependency or source file is required.";

@Override
public final ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException {
ImmutableListMultimap<BuildConfiguration, ObjcProvider> configurationToObjcDepMap =
ruleContext.getPrerequisitesByConfiguration("deps", Mode.SPLIT, ObjcProvider.class);
ImmutableListMultimap<BuildConfiguration, CppCompilationContext> configurationToCcDepMap =
ruleContext.getPrerequisitesByConfiguration("deps", Mode.SPLIT,
CppCompilationContext.class);
ImmutableListMultimap<BuildConfiguration, ObjcProvider> configurationToNonPropagatedObjcMap =
ruleContext.getPrerequisitesByConfiguration("non_propagated_deps", Mode.SPLIT,
ObjcProvider.class);

Set<BuildConfiguration> childConfigurations = getChildConfigurations(ruleContext);

IntermediateArtifacts ruleIntermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext);

NestedSetBuilder<Artifact> binariesToLipo =
NestedSetBuilder.<Artifact>stableOrder();
NestedSetBuilder<Artifact> filesToBuild =
NestedSetBuilder.<Artifact>stableOrder()
.add(ruleIntermediateArtifacts.combinedArchitectureBinary());

for (BuildConfiguration childConfig : childConfigurations) {
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext, childConfig);
ObjcCommon common = common(ruleContext, childConfig, intermediateArtifacts,
nullToEmptyList(configurationToObjcDepMap.get(childConfig)),
nullToEmptyList(configurationToNonPropagatedObjcMap.get(childConfig)),
nullToEmptyList(configurationToCcDepMap.get(childConfig)));
ObjcProvider objcProvider = common.getObjcProvider();
if (!hasLibraryOrSources(objcProvider)) {
ruleContext.ruleError(REQUIRES_AT_LEAST_ONE_LIBRARY_OR_SOURCE_FILE);
return null;
}
if (ruleContext.hasErrors()) {
return null;
}
binariesToLipo.add(intermediateArtifacts.strippedSingleArchitectureBinary());
new CompilationSupport(ruleContext, childConfig)
.registerCompileAndArchiveActions(common)
.registerLinkActions(
objcProvider, new ExtraLinkArgs(), ImmutableList.<Artifact>of(),
DsymOutputType.APP)
.validateAttributes();

if (ruleContext.hasErrors()) {
return null;
}
}

AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class);

new LipoSupport(ruleContext).registerCombineArchitecturesAction(
binariesToLipo.build(),
ruleIntermediateArtifacts.combinedArchitectureBinary(),
appleConfiguration.getIosCpuPlatform());

RuleConfiguredTargetBuilder targetBuilder =
ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build());

return targetBuilder.build();
}

private boolean hasLibraryOrSources(ObjcProvider objcProvider) {
return !Iterables.isEmpty(objcProvider.get(LIBRARY)) // Includes sources from this target.
|| !Iterables.isEmpty(objcProvider.get(IMPORTED_LIBRARY));
}

private ObjcCommon common(RuleContext ruleContext, BuildConfiguration buildConfiguration,
IntermediateArtifacts intermediateArtifacts, List<ObjcProvider> propagatedObjcDeps,
List<ObjcProvider> nonPropagatedObjcDeps, List<CppCompilationContext> cppDeps) {
CompilationArtifacts compilationArtifacts =
CompilationSupport.compilationArtifacts(ruleContext, intermediateArtifacts);

return new ObjcCommon.Builder(ruleContext, buildConfiguration)
.setCompilationAttributes(new CompilationAttributes(ruleContext))
.setResourceAttributes(new ResourceAttributes(ruleContext))
.setCompilationArtifacts(compilationArtifacts)
.addDefines(ruleContext.getTokenizedStringListAttr("defines"))
.addDepObjcProviders(propagatedObjcDeps)
.addDepCcHeaderProviders(cppDeps)
// TODO(cparsons): The below call only gets CC link params from one child configuration,
// as it is accessed via Mode.TARGET. This poses a problem with multi-architecture
// builds with CC dependencies.
.addDepCcLinkProviders(ruleContext)
.addDepObjcProviders(
ruleContext.getPrerequisites("bundles", Mode.TARGET, ObjcProvider.class))
.addNonPropagatedDepObjcProviders(nonPropagatedObjcDeps)
.setIntermediateArtifacts(intermediateArtifacts)
.setAlwayslink(false)
.setHasModuleMap()
.setLinkedBinary(intermediateArtifacts.strippedSingleArchitectureBinary())
.build();
}

private <T> List<T> nullToEmptyList(List<T> inputList) {
return inputList != null ? inputList : ImmutableList.<T>of();
}

private Set<BuildConfiguration> getChildConfigurations(RuleContext ruleContext) {
// This is currently a hack to obtain all child configurations regardless of the attribute
// values of this rule -- this rule does not currently use the actual info provided by
// this attribute. b/28403953 tracks cc toolchain usage.
ImmutableListMultimap<BuildConfiguration, CcToolchainProvider> configToProvider =
ruleContext.getPrerequisitesByConfiguration(":cc_toolchain", Mode.SPLIT,
CcToolchainProvider.class);

return configToProvider.keySet();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2016 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.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates;
import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;

import com.google.devtools.build.lib.analysis.BaseRuleClasses;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder;
import com.google.devtools.build.lib.rules.apple.AppleConfiguration;

/**
* Rule definition for apple_binary.
*/
public class AppleBinaryRule implements RuleDefinition {

/**
* Template for the fat binary output (using Apple's "lipo" tool to combine binaries of
* multiple architectures).
*/
private static final SafeImplicitOutputsFunction LIPOBIN = fromTemplates("%{name}_lipobin");

@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
.requiresConfigurationFragments(ObjcConfiguration.class, J2ObjcConfiguration.class,
AppleConfiguration.class)
.add(attr("$is_executable", BOOLEAN).value(true)
.nonconfigurable("Called from RunCommand.isExecutable, which takes a Target"))
.override(attr("deps", LABEL_LIST)
.cfg(IosApplication.SPLIT_ARCH_TRANSITION)
.direct_compile_time_input()
.allowedRuleClasses(ObjcRuleClasses.CompilingRule.ALLOWED_DEPS_RULE_CLASSES)
.allowedFileTypes())
.override(attr("non_propagated_deps", LABEL_LIST)
.direct_compile_time_input()
.cfg(IosApplication.SPLIT_ARCH_TRANSITION)
.allowedRuleClasses(ObjcRuleClasses.CompilingRule.ALLOWED_DEPS_RULE_CLASSES)
.allowedFileTypes())
.override(attr("srcs", LABEL_LIST)
.direct_compile_time_input()
.cfg(IosApplication.SPLIT_ARCH_TRANSITION)
.allowedFileTypes(ObjcRuleClasses.SRCS_TYPE))
// This is currently a hack to obtain all child configurations regardless of the attribute
// values of this rule -- this rule does not currently use the actual info provided by
// this attribute.
.add(attr(":cc_toolchain", LABEL)
.cfg(IosApplication.SPLIT_ARCH_TRANSITION)
.value(ObjcRuleClasses.APPLE_TOOLCHAIN))
.setImplicitOutputsFunction(
ImplicitOutputsFunction.fromFunctions(LIPOBIN))
.build();
}

@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("apple_binary")
.factoryClass(AppleBinary.class)
.ancestors(BaseRuleClasses.BaseRule.class, ObjcRuleClasses.LinkingRule.class,
ObjcRuleClasses.XcodegenRule.class, ObjcRuleClasses.SimulatorRule.class)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ static final class ExtraLinkArgs extends IterableWrapper<String> {
*/
// TODO(bazel-team): Remove this information from ObjcCommon and move it internal to this class.
static CompilationArtifacts compilationArtifacts(RuleContext ruleContext) {
return compilationArtifacts(ruleContext, ObjcRuleClasses.intermediateArtifacts(ruleContext));
}

/**
* Returns information about the given rule's compilation artifacts. Dependencies specified
* in the current rule's attributes are obtained via {@code ruleContext}. Output locations
* are determined using the given {@code intermediateArtifacts} object. The fact that these
* are distinct objects allows the caller to generate compilation actions pertaining to
* a configuration separate from the current rule's configuration.
*/
static CompilationArtifacts compilationArtifacts(RuleContext ruleContext,
IntermediateArtifacts intermediateArtifacts) {
PrerequisiteArtifacts srcs = ruleContext.getPrerequisiteArtifacts("srcs", Mode.TARGET)
.errorsForNonMatching(SRCS_TYPE);
return new CompilationArtifacts.Builder()
Expand All @@ -215,7 +227,7 @@ static CompilationArtifacts compilationArtifacts(RuleContext ruleContext) {
.list())
.addPrivateHdrs(srcs.filter(HEADERS).list())
.addPrecompiledSrcs(srcs.filter(PRECOMPILED_SRCS_TYPE).list())
.setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext))
.setIntermediateArtifacts(intermediateArtifacts)
.setPchFile(Optional.fromNullable(ruleContext.getPrerequisiteArtifact("pch", Mode.TARGET)))
.build();
}
Expand Down Expand Up @@ -1219,7 +1231,8 @@ CompilationSupport addXcodeSettings(Builder xcodeProviderBuilder, ObjcCommon com
.addHeaders(attributes.hdrs())
.addHeaders(attributes.textualHdrs())
.addUserHeaderSearchPaths(ObjcCommon.userHeaderSearchPaths(buildConfiguration))
.addHeaderSearchPaths("$(WORKSPACE_ROOT)", attributes.headerSearchPaths())
.addHeaderSearchPaths("$(WORKSPACE_ROOT)",
attributes.headerSearchPaths(buildConfiguration.getGenfilesFragment()))
.addHeaderSearchPaths("$(WORKSPACE_ROOT)", includeSystemPaths)
.addHeaderSearchPaths("$(SDKROOT)/usr/include", attributes.sdkIncludes())
.addNonPropagatedHeaderSearchPaths(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2016 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.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.rules.apple.Platform;

/**
* Support for registering actions using the Apple tool "lipo", which combines artifacts of
* different architectures to make multi-architecture artifacts.
*/
public class LipoSupport {
private final RuleContext ruleContext;

public LipoSupport(RuleContext ruleContext) {
this.ruleContext = ruleContext;
}

/**
* Registers an action to invoke "lipo" on all artifacts in {@code inputBinaries} to create the
* {@code outputBinary} multi-architecture artifact, built for platform {@code platform}.
*/
public void registerCombineArchitecturesAction(NestedSet<Artifact> inputBinaries,
Artifact outputBinary, Platform platform) {

ruleContext.registerAction(ObjcRuleClasses.spawnAppleEnvActionBuilder(ruleContext, platform)
.setMnemonic("ObjcCombiningArchitectures")
.addTransitiveInputs(inputBinaries)
.addOutput(outputBinary)
.setExecutable(CompilationSupport.xcrunwrapper(ruleContext))
.setCommandLine(CustomCommandLine.builder()
.add(ObjcRuleClasses.LIPO)
.addExecPaths("-create", inputBinaries)
.addExecPath("-o", outputBinary)
.build())
.build(ruleContext));
}
}
Loading

0 comments on commit b5a76c5

Please sign in to comment.