diff options
7 files changed, 209 insertions, 8 deletions
diff --git a/scripts/bootstrap/compile.sh b/scripts/bootstrap/compile.sh index bf1da4dd81..1948943e1f 100755 --- a/scripts/bootstrap/compile.sh +++ b/scripts/bootstrap/compile.sh @@ -254,7 +254,14 @@ chmod 0755 ${ARCHIVE_DIR}/_embedded_binaries/process-wrapper${EXE_EXT} cp src/main/tools/build_interface_so ${ARCHIVE_DIR}/_embedded_binaries/build_interface_so cp src/main/tools/jdk.BUILD ${ARCHIVE_DIR}/_embedded_binaries/jdk.BUILD cp $OUTPUT_DIR/libblaze.jar ${ARCHIVE_DIR} -cp tools/osx/xcode_locator_stub.sh ${ARCHIVE_DIR}/_embedded_binaries/xcode-locator + +# TODO(b/28965185): Remove when xcode-locator is no longer required in embedded_binaries. +log "Compiling xcode-locator..." +if [[ $PLATFORM == "darwin" ]]; then + run /usr/bin/xcrun clang -fobjc-arc -framework CoreServices -framework Foundation -o ${ARCHIVE_DIR}/_embedded_binaries/xcode-locator tools/osx/xcode_locator.m +else + cp tools/osx/xcode_locator_stub.sh ${ARCHIVE_DIR}/_embedded_binaries/xcode-locator +fi # bazel build using bootstrap version function bootstrap_build() { diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java index 4cf042b782..26fd3af4b5 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java @@ -188,6 +188,8 @@ public class BazelRulesModule extends BlazeModule { // will not be loaded for our Java tests. builder.addWorkspaceFileSuffix( ResourceFileLoader.loadResource(BazelCppRuleClasses.class, "cc_configure.WORKSPACE")); + builder.addWorkspaceFileSuffix( + ResourceFileLoader.loadResource(BazelRulesModule.class, "xcode_configure.WORKSPACE")); } catch (IOException e) { throw new IllegalStateException(e); } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/xcode_configure.WORKSPACE b/src/main/java/com/google/devtools/build/lib/bazel/rules/xcode_configure.WORKSPACE new file mode 100644 index 0000000000..efc6fe6e36 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/xcode_configure.WORKSPACE @@ -0,0 +1,2 @@ +load("@bazel_tools//tools/osx:xcode_configure.bzl", "xcode_configure") +xcode_configure("@bazel_tools//tools/osx:xcode_locator.m") 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 c110cbbd49..b014ecc7fd 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 @@ -26,7 +26,6 @@ import com.google.devtools.build.lib.rules.apple.Platform.PlatformType; import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; import com.google.devtools.common.options.EnumConverter; import com.google.devtools.common.options.Option; - import java.util.List; /** @@ -150,18 +149,22 @@ public class AppleCommandLineOptions extends FragmentOptions { converter = DefaultProvisioningProfileConverter.class) public Label defaultProvisioningProfile; - @Option(name = "xcode_version_config", - defaultValue = "@bazel_tools" + DEFAULT_XCODE_VERSION_CONFIG_LABEL, - category = "undocumented", - converter = LabelConverter.class, - help = "The label of the xcode_config rule to be used for selecting the xcode version " - + "in the build configuration") + @Option( + name = "xcode_version_config", + defaultValue = "@local_config_xcode//:host_xcodes", + category = "undocumented", + converter = LabelConverter.class, + help = + "The label of the xcode_config rule to be used for selecting the xcode version " + + "in the build configuration" + ) public Label xcodeVersionConfig; /** * The default label of the build-wide {@code xcode_config} configuration rule. This can be * changed from the default using the {@code xcode_version_config} build flag. */ + // TODO(cparsons): Update all callers to reference the actual xcode_version_config flag value. static final String DEFAULT_XCODE_VERSION_CONFIG_LABEL = "//tools/objc:host_xcodes"; /** Converter for --default_ios_provisioning_profile. */ diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java index a17305be3f..5b47d44f86 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java @@ -64,6 +64,7 @@ public final class BazelAnalysisMock extends AnalysisMock { new ArrayList<>( ImmutableList.of( "local_repository(name = 'bazel_tools', path = '" + bazelToolWorkspace + "')", + "local_repository(name = 'local_config_xcode', path = '/local_config_xcode')", "bind(", " name = 'objc_proto_lib',", " actual = '//objcproto:ProtocolBuffers_lib',", @@ -79,6 +80,8 @@ public final class BazelAnalysisMock extends AnalysisMock { "bind(name = 'android/sdk', actual='@bazel_tools//tools/android:sdk')", "bind(name = 'tools/python', actual='//tools/python')")); + config.create( + "/local_config_xcode/BUILD", "xcode_config(name = 'host_xcodes')"); config.overwrite("WORKSPACE", workspaceContents.toArray(new String[workspaceContents.size()])); config.create("/bazel_tools_workspace/WORKSPACE", "workspace(name = 'bazel_tools')"); config.create( diff --git a/src/test/shell/bazel/bazel_apple_test.sh b/src/test/shell/bazel/bazel_apple_test.sh index 8495c15dba..3c35f9813b 100755 --- a/src/test/shell/bazel/bazel_apple_test.sh +++ b/src/test/shell/bazel/bazel_apple_test.sh @@ -547,4 +547,36 @@ apple_watch2_extension( EOF } +function test_host_xcodes() { + XCODE_VERSION=$(xcodebuild -version | grep "Xcode" \ + | sed -E "s/Xcode (([0-9]|.)+).*/\1/") + IOS_SDK=$(xcodebuild -version -sdk | grep iphoneos \ + | sed -E "s/.*\(iphoneos(([0-9]|.)+)\).*/\1/") + MACOSX_SDK=$(xcodebuild -version -sdk | grep macosx \ + | sed -E "s/.*\(macosx(([0-9]|.)+)\).*/\1/" | head -n 1) + + # Unfortunately xcodebuild -version doesn't always pad with trailing .0, so, + # for example, may produce "6.4", which is bad for this test. + if [[ ! $XCODE_VERSION =~ [0-9].[0-9].[0-9] ]] + then + XCODE_VERSION="${XCODE_VERSION}.0" + fi + + bazel build @local_config_xcode//:host_xcodes >"${TEST_log}" 2>&1 \ + || fail "Expected host_xcodes to build" + + bazel query "attr(version, $XCODE_VERSION, \ + attr(default_ios_sdk_version, $IOS_SDK, \ + attr(default_macosx_sdk_version, $MACOSX_SDK, \ + labels('versions', '@local_config_xcode//:host_xcodes'))))" \ + > xcode_version_target + + assert_contains "local_config_xcode" xcode_version_target + + DEFAULT_LABEL=$(bazel query \ + "labels('default', '@local_config_xcode//:host_xcodes')") + + assert_equals $DEFAULT_LABEL $(cat xcode_version_target) +} + run_suite "apple_tests" diff --git a/tools/osx/xcode_configure.bzl b/tools/osx/xcode_configure.bzl new file mode 100644 index 0000000000..4315a045ed --- /dev/null +++ b/tools/osx/xcode_configure.bzl @@ -0,0 +1,152 @@ +# 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. +"""Repository rule to generate host xcode_config and xcode_version targets. + + The xcode_config and xcode_version targets are configured for xcodes/SDKs + installed on the local host. +""" + + +def _search_string(fullstring, prefix, suffix): + """Returns the substring between two given substrings of a larger string. + + Args: + fullstring: The larger string to search. + prefix: The substring that should occur directly before the returned string. + suffix: The substring that should occur direclty after the returned string. + Returns: + A string occurring in fullstring exactly prefixed by prefix, and exactly + terminated by suffix. For example, ("hello goodbye", "lo ", " bye") will + return "good". If there is no such string, returns the empty string. + """ + + prefix_index = fullstring.find(prefix) + if (prefix_index < 0): + return "" + result_start_index = prefix_index + len(prefix) + suffix_index = fullstring.find(suffix, result_start_index) + if (suffix_index < 0): + return "" + return fullstring[result_start_index:suffix_index] + + +def _search_sdk_output(output, sdkname): + """Returns the sdk version given xcodebuild stdout and an sdkname.""" + return _search_string(output, "(%s" % sdkname, ")") + + +def _xcode_version_output(repository_ctx, name, version, aliases, developer_dir): + """Returns a string containing an xcode_version build target.""" + build_contents = "" + decorated_aliases = [] + for alias in aliases: + decorated_aliases.append("'%s'" % alias) + xcodebuild_result = repository_ctx.execute(["xcodebuild", "-version", "-sdk"], 5, {"DEVELOPER_DIR": developer_dir}) + if (xcodebuild_result.return_code != 0): + print("Invoking xcodebuild failed, return code %s, stderr: %s", xcodebuild_result.return_code, xcodebuild_result.stderr) + ios_sdk_version = _search_sdk_output(xcodebuild_result.stdout, "iphoneos") + tvos_sdk_version = _search_sdk_output(xcodebuild_result.stdout, "appletvos") + macosx_sdk_version = _search_sdk_output(xcodebuild_result.stdout, "macosx") + watchos_sdk_version = _search_sdk_output(xcodebuild_result.stdout, "watchos") + build_contents += "xcode_version(\n name = '%s'," % name + build_contents += "\n version = '%s'," % version + if aliases: + build_contents += "\n aliases = [%s]," % " ,".join(decorated_aliases) + if ios_sdk_version: + build_contents += "\n default_ios_sdk_version = '%s'," % ios_sdk_version + if tvos_sdk_version: + build_contents += "\n default_tvos_sdk_version = '%s'," % tvos_sdk_version + if macosx_sdk_version: + build_contents += "\n default_macosx_sdk_version = '%s'," % macosx_sdk_version + if watchos_sdk_version: + build_contents += "\n default_watchos_sdk_version = '%s'," % watchos_sdk_version + build_contents += "\n)\n" + return build_contents + + +def _darwin_build_file(repository_ctx): + """Evaluates local system state to create xcode_config and xcode_version targets.""" + xcodeloc_src_path = str(repository_ctx.path(Label(repository_ctx.attr.xcode_locator))) + repository_ctx.execute(["xcrun", "clang", "-fobjc-arc", "-framework", "CoreServices", "-framework", "Foundation", "-o", "xcode-locator-bin", xcodeloc_src_path]) + + xcode_locator_result = repository_ctx.execute(["./xcode-locator-bin", "-v"]) + if (xcode_locator_result.return_code != 0): + print("Invoking xcode-locator failed, return code %s, stderr: %s", xcode_locator_result.return_code, xcode_locator_result.stderr) + xcodebuild_result = repository_ctx.execute(["xcodebuild", "-version"]) + if (xcodebuild_result.return_code != 0): + print("Invoking xcodebuild failed, return code %s, stderr: %s", xcodebuild_result.return_code, xcodebuild_result.stderr) + + default_xcode_version = _search_string(xcodebuild_result.stdout, "Xcode ", "\n") + default_xcode_target = "" + target_names = [] + buildcontents = "" + + # xcode_dump is comprised of newlines with different installed xcode versions, + # each line of the form <version>:<comma_separated_aliases>:<developer_dir>. + xcode_dump = xcode_locator_result.stdout + for xcodeversion in xcode_dump.split("\n"): + if ":" in xcodeversion: + infosplit = xcodeversion.split(":") + version = infosplit[0] + aliases = infosplit[1].split(",") + developer_dir = infosplit[2] + target_name = "version%s" % version.replace(".", "_") + buildcontents += _xcode_version_output(repository_ctx, target_name, version, aliases, developer_dir) + target_names.append("':%s'" % target_name) + if (version == default_xcode_version or default_xcode_version in aliases): + default_xcode_target = target_name + buildcontents += "xcode_config(name = 'host_xcodes'," + if target_names: + buildcontents += "\n versions = [%s]," % ", ".join(target_names) + if default_xcode_target: + buildcontents += "\n default = ':%s'," % default_xcode_target + buildcontents += "\n)\n" + return buildcontents + + +def _impl(repository_ctx): + """Implementation for the local_config_xcode repository rule. + + Generates a BUILD file containing a root xcode_config target named 'host_xcodes', + which points to an xcode_version target for each version of xcode installed on + the local host machine. If no versions of xcode are present on the machine + (for instance, if this is a non-darwin OS), creates a stub target. + + Args: + repository_ctx: The repository context. + """ + + os_name = repository_ctx.os.name.lower() + build_contents = "package(default_visibility = ['//visibility:public'])\n\n" + if (os_name.startswith("mac os")): + build_contents += _darwin_build_file(repository_ctx) + else: + build_contents += "xcode_config(name = 'host_xcodes')" + repository_ctx.file("BUILD", build_contents) + +xcode_autoconf = repository_rule( + implementation=_impl, + local=True, + attrs={ + "xcode_locator": attr.string(), + } +) + + +def xcode_configure(xcode_locator_label): + """Generates a repository containing host xcode version information.""" + xcode_autoconf( + name="local_config_xcode", + xcode_locator=xcode_locator_label + ) |