diff options
author | 2015-11-25 00:30:11 +0000 | |
---|---|---|
committer | 2015-11-25 10:00:26 +0000 | |
commit | cd0881d8b56024ad2157a893609233761946ba11 (patch) | |
tree | 8d51323cabe62793907a23787d835960d652ab80 /src/main/java/com/google/devtools/build/lib/rules/apple | |
parent | f0d78494f35c21426dec1608717e3c4aad72ec40 (diff) |
Implementation for xcode_version and xcode_config rules.
--
MOS_MIGRATED_REVID=108659943
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/apple')
8 files changed, 473 insertions, 6 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java index 9c2dca458f..f9e9e47626 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java @@ -39,6 +39,8 @@ public class AppleCommandLineOptions extends FragmentOptions { "If specified, uses xcode of the given version for relevant build actions. " + "If unspecified, uses the executor default version of xcode." ) + // TODO(bazel-team): This should be of String type, to allow referencing an alias based + // on an xcode_config target. public DottedVersion xcodeVersion; @Option( diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java index b751a13f6f..b63f4f30de 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java @@ -70,11 +70,13 @@ public class AppleConfiguration extends BuildConfiguration.Fragment { } /** - * Returns the value of the xcode version override build flag, if specified. This is directly - * derived from --xcode_version_override. Format "x(.y)(.z)" (for example, "7", or "6.4", - * or "7.0.1"). + * Returns the value of the xcode version build flag if available. This is obtained directly from + * the {@code --xcode_version} build flag. + * + * <p>Most rules should avoid using this flag value, and instead obtain the appropriate xcode + * version from {@link XcodeConfigProvider#getXcodeVersion}. */ - public Optional<DottedVersion> getXcodeVersionOverride() { + public Optional<DottedVersion> getXcodeVersionOverrideFlag() { return xcodeVersionOverride; } @@ -83,6 +85,7 @@ public class AppleConfiguration extends BuildConfiguration.Fragment { * for actions pertaining to building ios applications. Keys are variable names and values are * their corresponding values. */ + // TODO(bazel-team): Repurpose for non-ios platforms. // TODO(bazel-team): Separate host system and target platform environment public Map<String, String> getEnvironmentForIosAction() { ImmutableMap.Builder<String, String> mapBuilder = ImmutableMap.builder(); @@ -98,9 +101,10 @@ public class AppleConfiguration extends BuildConfiguration.Fragment { */ public Map<String, String> appleHostSystemEnv() { ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); - if (getXcodeVersionOverride().isPresent()) { + // TODO(bazel-team): Use the xcode version from transitive target info instead of the flag. + if (getXcodeVersionOverrideFlag().isPresent()) { builder.put(AppleConfiguration.XCODE_VERSION_ENV_NAME, - getXcodeVersionOverride().get().toString()); + getXcodeVersionOverrideFlag().get().toString()); } return builder.build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfig.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfig.java new file mode 100644 index 0000000000..9528bdafea --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfig.java @@ -0,0 +1,135 @@ +// Copyright 2015 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.apple; + +import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; + +import com.google.common.base.Joiner; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +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.RunfilesProvider; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; + +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * Implementation for the {@code xcode_config} rule. + */ +public class XcodeConfig implements RuleConfiguredTargetFactory { + + @Override + public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { + AppleConfiguration configuration = ruleContext.getFragment(AppleConfiguration.class); + Optional<DottedVersion> versionOverrideFlag = configuration.getXcodeVersionOverrideFlag(); + + DottedVersion targetVersion = resolveExplicitlyDefinedVersion(ruleContext); + + XcodeConfigProvider xcodeConfigProvider; + if (targetVersion == null) { + if (versionOverrideFlag.isPresent()) { + xcodeConfigProvider = new XcodeConfigProvider(versionOverrideFlag.get()); + } else { + xcodeConfigProvider = XcodeConfigProvider.hostSystemDefault(); + } + } else { + xcodeConfigProvider = new XcodeConfigProvider(targetVersion); + } + + return new RuleConfiguredTargetBuilder(ruleContext) + .addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY) + .addProvider(XcodeConfigProvider.class, xcodeConfigProvider) + .build(); + } + + /** + * Returns the xcode version number corresponding to the {@code --xcode_version} flag, if there + * is an available {@code xcode_version} target which recognizes the flag value as either + * an official version or an alias. Returns null if no such target is found. + */ + @Nullable private DottedVersion resolveExplicitlyDefinedVersion(RuleContext ruleContext) { + AppleConfiguration configuration = ruleContext.getFragment(AppleConfiguration.class); + Optional<DottedVersion> versionOverrideFlag = configuration.getXcodeVersionOverrideFlag(); + if (versionOverrideFlag.isPresent()) { + // The version override flag is not necessarily an actual version - it may be a version + // alias. + DottedVersion explicitVerison = + aliasesToVersionMap(ruleContext).get(versionOverrideFlag.get().toString()); + if (explicitVerison != null) { + return explicitVerison; + } + } else { // No override specified. Use default. + XcodeVersionProvider defaultProvider = ruleContext.getPrerequisite( + XcodeConfigRule.DEFAULT_ATTR_NAME, Mode.TARGET, XcodeVersionProvider.class); + + if (defaultProvider != null) { + return defaultProvider.getVersion(); + } + } + + boolean requireDefinedVersions = ruleContext.attributes().get( + XcodeConfigRule.REQUIRE_DEFINED_VERSIONS_ATTR_NAME, BOOLEAN); + if (requireDefinedVersions) { + ruleContext.ruleError( + "xcode version config required an explicitly defined version, but none was available"); + } + + return null; + } + + private static Map<String, DottedVersion> aliasesToVersionMap(RuleContext ruleContext) { + Iterable<XcodeVersionProvider> xcodeVersionProviders = + ruleContext.getPrerequisites(XcodeConfigRule.VERSIONS_ATTR_NAME, Mode.TARGET, + XcodeVersionProvider.class); + + Map<String, DottedVersion> aliasesToVersionMap = Maps.newLinkedHashMap(); + for (XcodeVersionProvider versionProvider : xcodeVersionProviders) { + for (String alias : versionProvider.getAliases()) { + if (aliasesToVersionMap.put(alias, versionProvider.getVersion()) != null) { + ruleErrorDuplicateAlias(alias, ruleContext); + } + } + if (aliasesToVersionMap.put( + versionProvider.getVersion().toString(), versionProvider.getVersion()) != null) { + ruleErrorDuplicateAlias(versionProvider.getVersion().toString(), ruleContext); + } + } + return aliasesToVersionMap; + } + + private static void ruleErrorDuplicateAlias(String alias, RuleContext ruleContext) { + Iterable<XcodeVersionProvider> xcodeVersionProviders = + ruleContext.getPrerequisites(XcodeConfigRule.VERSIONS_ATTR_NAME, Mode.TARGET, + XcodeVersionProvider.class); + + ImmutableList.Builder<Label> labelsContainingAlias = ImmutableList.builder(); + for (XcodeVersionProvider versionProvider : xcodeVersionProviders) { + if (versionProvider.getAliases().contains(alias) + || versionProvider.getVersion().toString().equals(alias)) { + labelsContainingAlias.add(versionProvider.getLabel()); + } + } + ruleContext.ruleError(String.format( + "'%s' is registered to multiple labels (%s) in a single xcode version " + + "configuration", alias, Joiner.on(", ").join(labelsContainingAlias.build()))); + } +}
\ No newline at end of file diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfigProvider.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfigProvider.java new file mode 100644 index 0000000000..5241e04867 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfigProvider.java @@ -0,0 +1,53 @@ +// Copyright 2015 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.apple; + +import com.google.common.base.Optional; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; + +/** + * Provides a version of xcode based on a combination of the {@code --xcode_version} build flag + * and a {@code xcode_config} target. This version of xcode should be used for selecting apple + * toolchains and SDKs. + */ +@Immutable +public final class XcodeConfigProvider implements TransitiveInfoProvider { + private final Optional<DottedVersion> xcodeVersion; + + XcodeConfigProvider(DottedVersion xcodeVersion) { + this.xcodeVersion = Optional.of(xcodeVersion); + } + + private XcodeConfigProvider() { + this.xcodeVersion = Optional.absent(); + } + + /** + * Returns a {@link XcodeConfigProvider} with no xcode version specified. The host system + * default xcode should be used. See {@link #getXcodeVersion}. + */ + static XcodeConfigProvider hostSystemDefault() { + return new XcodeConfigProvider(); + } + + /** + * Returns either an explicit xcode version which should be used in actions which require an + * apple toolchain, or {@link Optional#absent} if the host system default should be used. + */ + public Optional<DottedVersion> getXcodeVersion() { + return xcodeVersion; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfigRule.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfigRule.java new file mode 100644 index 0000000000..b48a651e18 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeConfigRule.java @@ -0,0 +1,91 @@ +// Copyright 2015 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.apple; + +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.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.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder; + +/** + * Rule definition for {@code xcode_config} rule. + */ +public class XcodeConfigRule implements RuleDefinition { + + static final String DEFAULT_ATTR_NAME = "default"; + static final String VERSIONS_ATTR_NAME = "versions"; + static final String REQUIRE_DEFINED_VERSIONS_ATTR_NAME = "require_defined_version"; + + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { + return builder + .requiresConfigurationFragments(AppleConfiguration.class) + /* <!-- #BLAZE_RULE(proto_library).ATTRIBUTE(version) --> + The default official version of xcode to use. + ${SYNOPSIS} + The version specified by the provided <code>xcode_version</code> target is to be used if + no <code>xcode_version</code> build flag is specified. + <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ + .add(attr(DEFAULT_ATTR_NAME, LABEL) + .allowedRuleClasses("xcode_version") + .allowedFileTypes()) + /* <!-- #BLAZE_RULE(proto_library).ATTRIBUTE(version) --> + Accepted <code>xcode_version<code> targets that may be used. + ${SYNOPSIS} + If the value of the <code>xcode_version</code> build flag matches one of the aliases + or version number of any of the given <code>xcode_version</code> targets, the matching + target will be used. + <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ + .add(attr(VERSIONS_ATTR_NAME, LABEL_LIST) + .allowedRuleClasses("xcode_version") + .allowedFileTypes()) + /* <!-- #BLAZE_RULE(proto_library).ATTRIBUTE(version) --> + Whether to require the build's xcode version match one of the declared targets. + ${SYNOPSIS} + If true, this will raise an error if either the <code>xcode_version</code> flag value + or <code>default</code> attribute value do not match one of the versions declared + among <code>xcode_verison</code> targets. + <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ + .add(attr(REQUIRE_DEFINED_VERSIONS_ATTR_NAME, BOOLEAN).value(false)) + .build(); + } + + @Override + public Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("xcode_config") + .ancestors(BaseRuleClasses.BaseRule.class) + .factoryClass(XcodeConfig.class) + .build(); + } +} + +/*<!-- #BLAZE_RULE (NAME = xcode_config, TYPE = OTHER, FAMILY = Workspace)[GENERIC_RULE] --> + +${ATTRIBUTE_SIGNATURE} + +<p>A single target of this rule can be referenced by the <code>--xcode_config</code> build flag to +translate the <code>--xcode_version</code> flag into an accepted official xcode version. This +allows selection of a an official xcode version from a number of registered aliases.</p> + +${ATTRIBUTE_DEFINITION} + +<!-- #END_BLAZE_RULE -->*/ diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersion.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersion.java new file mode 100644 index 0000000000..2b363e6c62 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersion.java @@ -0,0 +1,46 @@ +// Copyright 2015 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.apple; + +import static com.google.devtools.build.lib.syntax.Type.STRING; +import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; + +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; + +import java.util.List; + +/** + * Implementation for the {@code xcode_version} rule. + */ +public class XcodeVersion implements RuleConfiguredTargetFactory { + + @Override + public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { + DottedVersion version = DottedVersion.fromString( + ruleContext.attributes().get(XcodeVersionRule.VERSION_ATTR_NAME, STRING)); + List<String> aliases = + ruleContext.attributes().get(XcodeVersionRule.ALIASES_ATTR_NAME, STRING_LIST); + + return new RuleConfiguredTargetBuilder(ruleContext) + .addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY) + .addProvider(XcodeVersionProvider.class, + new XcodeVersionProvider(ruleContext.getLabel(), version, aliases)) + .build(); + } +}
\ No newline at end of file diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProvider.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProvider.java new file mode 100644 index 0000000000..f7b15b60d6 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProvider.java @@ -0,0 +1,64 @@ +// Copyright 2015 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.apple; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; + +import java.util.List; + +/** + * Provides the information in a single {@code xcode_version} target. A single target of this rule + * contains an official version label decided by Apple and a number of supported aliases one might + * use to reference this version. + * + * <p>For example, one may want to reference official xcode version 7.0.1 using the "7" or + * "7.0" aliases. + */ +@Immutable +public final class XcodeVersionProvider implements TransitiveInfoProvider { + private final Label label; + private final DottedVersion version; + private final ImmutableList<String> aliases; + + XcodeVersionProvider(Label label, DottedVersion version, List<String> aliases) { + this.label = label; + this.version = version; + this.aliases = ImmutableList.copyOf(aliases); + } + + /** + * Returns the label of the owning target of this provider. + */ + public Label getLabel() { + return label; + } + + /** + * Returns the official xcode version the owning {@code xcode_version} target is referencing. + */ + public DottedVersion getVersion() { + return version; + } + + /** + * Returns the accepted string aliases for this xcode version. + */ + public List<String> getAliases() { + return aliases; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionRule.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionRule.java new file mode 100644 index 0000000000..0248fce3d7 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionRule.java @@ -0,0 +1,72 @@ +// Copyright 2015 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.apple; + +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.syntax.Type.STRING; +import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; + +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.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder; + +/** + * Rule definition for {@code xcode_version} rule. + */ +public class XcodeVersionRule implements RuleDefinition { + + static final String VERSION_ATTR_NAME = "version"; + static final String ALIASES_ATTR_NAME = "aliases"; + + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { + return builder + .requiresConfigurationFragments(AppleConfiguration.class) + /* <!-- #BLAZE_RULE(proto_library).ATTRIBUTE(version) --> + The official version number of a version of Xcode. + <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ + .add(attr(VERSION_ATTR_NAME, STRING).mandatory()) + /* <!-- #BLAZE_RULE(proto_library).ATTRIBUTE(version) --> + Accepted aliases for this version of Xcode. + ${SYNOPSIS} + If the value of the <code>xcode_version</code> build flag matches any of the given + alias strings, this xcode version will be used. + <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ + .add(attr(ALIASES_ATTR_NAME, STRING_LIST)) + .build(); + } + + @Override + public Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("xcode_version") + .ancestors(BaseRuleClasses.BaseRule.class) + .factoryClass(XcodeVersion.class) + .build(); + } +} + +/*<!-- #BLAZE_RULE (NAME = xcode_version, TYPE = OTHER, FAMILY = Workspace)[GENERIC_RULE] --> + +${ATTRIBUTE_SIGNATURE} + +<p>Represents a single official xcode version with acceptable aliases for that xcode version. +See the <code>xcode_config</code> rule.</p> + +${ATTRIBUTE_DEFINITION} + +<!-- #END_BLAZE_RULE -->*/ |