diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java | 1234 |
1 files changed, 1234 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java new file mode 100644 index 0000000000..4dea91f381 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcSkylarkTest.java @@ -0,0 +1,1234 @@ +// 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.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.rules.objc.BundleableFile.BUNDLED_FIELD; +import static com.google.devtools.build.lib.rules.objc.BundleableFile.BUNDLE_PATH_FIELD; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ObjectArrays; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.util.ActionsTestUtil; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.rules.apple.AppleToolchain; +import com.google.devtools.build.lib.rules.apple.DottedVersion; +import com.google.devtools.build.lib.rules.objc.ObjcCommandLineOptions.ObjcCrosstoolMode; +import com.google.devtools.build.lib.syntax.SkylarkDict; +import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for Skylark interaction with the objc_* rules. + */ +@RunWith(JUnit4.class) +public class ObjcSkylarkTest extends ObjcRuleTestCase { + + @Override + protected void useConfiguration(String... args) throws Exception { + // Do not test crosstool for skylark tests. + useConfiguration(ObjcCrosstoolMode.OFF, args); + } + + @Test + public void testSkylarkRuleCanDependOnNativeAppleRule() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def my_rule_impl(ctx):", + " dep = ctx.attr.deps[0]", + " return struct(", + " found_libs = dep.objc.library,", + " found_hdrs = dep.objc.header,", + " )", + "my_rule = rule(implementation = my_rule_impl,", + " attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False, providers = ['objc']),", + "})"); + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'my_rule')", + "my_rule(", + " name = 'my_target',", + " deps = [':lib'],", + ")", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + " hdrs = ['b.h']", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + SkylarkNestedSet skylarkLibraries = + (SkylarkNestedSet) skylarkTarget.get("found_libs"); + SkylarkNestedSet skylarkHdrs = + (SkylarkNestedSet) skylarkTarget.get("found_hdrs"); + + assertThat(ActionsTestUtil.baseArtifactNames(skylarkLibraries.getSet(Artifact.class))) + .contains("liblib.a"); + assertThat(ActionsTestUtil.baseArtifactNames(skylarkHdrs.getSet(Artifact.class))) + .contains("b.h"); + } + + @Test + public void testSkylarkProviderRetrievalNoneIfNoProvider() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def my_rule_impl(ctx):", + " dep = ctx.attr.deps[0]", + " objc_provider = dep.objc", + " return struct()", + "my_rule = rule(implementation = my_rule_impl,", + " attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False),", + "})"); + scratch.file("examples/apple_skylark/a.cc"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'my_rule')", + "my_rule(", + " name = 'my_target',", + " deps = [':lib'],", + ")", + "cc_library(", + " name = 'lib',", + " srcs = ['a.cc'],", + " hdrs = ['b.h']", + ")"); + try { + getConfiguredTarget("//examples/apple_skylark:my_target"); + fail("Should throw assertion error"); + } catch (AssertionError e) { + assertThat(e) + .hasMessageThat() + .contains("File \"/workspace/examples/apple_skylark/BUILD\", line 3"); + assertThat(e).hasMessageThat().contains("my_rule(name = 'my_target')"); + assertThat(e) + .hasMessageThat() + .contains("File \"/workspace/examples/rule/apple_rules.bzl\", line 3, in my_rule_impl"); + assertThat(e).hasMessageThat().contains("dep.objc"); + assertThat(e) + .hasMessageThat() + .contains("target (rule class of 'cc_library') doesn't have provider 'objc'."); + } + } + + @Test + public void testSkylarkProviderCanCheckForExistanceOfObjcProvider() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def my_rule_impl(ctx):", + " cc_has_provider = hasattr(ctx.attr.deps[0], 'objc')", + " objc_has_provider = hasattr(ctx.attr.deps[1], 'objc')", + " return struct(cc_has_provider=cc_has_provider, objc_has_provider=objc_has_provider)", + "my_rule = rule(implementation = my_rule_impl,", + " attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False),", + "})"); + scratch.file("examples/apple_skylark/a.cc"); + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'my_rule')", + "my_rule(", + " name = 'my_target',", + " deps = [':cc_lib', ':objc_lib'],", + ")", + "objc_library(", + " name = 'objc_lib',", + " srcs = ['a.m'],", + ")", + "cc_library(", + " name = 'cc_lib',", + " srcs = ['a.cc'],", + ")"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + boolean ccResult = + (boolean) skylarkTarget.get("cc_has_provider"); + boolean objcResult = + (boolean) skylarkTarget.get("objc_has_provider"); + assertThat(ccResult).isFalse(); + assertThat(objcResult).isTrue(); + } + + @Test + public void testSkylarkExportsObjcProviderToNativeRule() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def my_rule_impl(ctx):", + " dep = ctx.attr.deps[0]", + " objc_provider = dep.objc", + " return struct(objc=objc_provider)", + "swift_library = rule(implementation = my_rule_impl,", + " attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False, providers = ['objc'])", + "})"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'swift_library')", + "swift_library(", + " name='my_target',", + " deps=[':lib'],", + ")", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + " defines = ['mock_define']", + ")", + "objc_binary(", + " name = 'bin',", + " deps = [':my_target']", + ")"); + + ConfiguredTarget binaryTarget = getConfiguredTarget("//examples/apple_skylark:bin"); + ObjcProvider objcProvider = binaryTarget.getProvider(ObjcProvider.class); + + assertThat(Artifact.toRootRelativePaths(objcProvider.get(ObjcProvider.LIBRARY))) + .contains("examples/apple_skylark/liblib.a"); + assertThat(objcProvider.get(ObjcProvider.DEFINE)).contains("mock_define"); + } + + @Test + public void testObjcRuleCanDependOnArbitrarySkylarkRuleThatProvidesObjc() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def my_rule_impl(ctx):", + " objc_provider = apple_common.new_objc_provider(define=depset(['mock_define']))", + " return struct(objc=objc_provider)", + "my_rule = rule(implementation = my_rule_impl,", + " attrs = {})"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'my_rule')", + "my_rule(", + " name='my_target'", + ")", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + " deps = [':my_target']", + ")", + "objc_binary(", + " name = 'bin',", + " deps = [':lib']", + ")"); + + ConfiguredTarget binaryTarget = getConfiguredTarget("//examples/apple_skylark:bin"); + ObjcProvider objcProvider = binaryTarget.getProvider(ObjcProvider.class); + + assertThat(objcProvider.get(ObjcProvider.DEFINE)).contains("mock_define"); + } + + @Test + public void testSkylarkCanAccessAppleConfiguration() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def swift_binary_impl(ctx):", + " cpu = ctx.fragments.apple.ios_cpu()", + " platform = ctx.fragments.apple.ios_cpu_platform()", + " env = ctx.fragments.apple.target_apple_env(platform)", + " xcode_version = ctx.fragments.apple.xcode_version()", + " sdk_version = ctx.fragments.apple.sdk_version_for_platform(platform)", + " single_arch_platform = ctx.fragments.apple.single_arch_platform", + " single_arch_cpu = ctx.fragments.apple.single_arch_cpu", + " platform_type = single_arch_platform.platform_type", + " bitcode_mode = ctx.fragments.apple.bitcode_mode", + " return struct(", + " cpu=cpu,", + " env=env,", + " xcode_version=str(xcode_version),", + " sdk_version=str(sdk_version),", + " single_arch_platform=str(single_arch_platform),", + " single_arch_cpu=str(single_arch_cpu),", + " platform_type=str(platform_type),", + " bitcode_mode=str(bitcode_mode)", + " )", + "swift_binary = rule(", + "implementation = swift_binary_impl,", + "fragments = ['apple']", + ")"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'swift_binary')", + "swift_binary(", + " name='my_target',", + ")"); + + useConfiguration("--cpu=ios_i386", "--xcode_version=7.1"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + + Object iosCpu = skylarkTarget.get("cpu"); + @SuppressWarnings("unchecked") + SkylarkDict<String, String> env = + (SkylarkDict<String, String>) skylarkTarget.get("env"); + Object sdkVersion = skylarkTarget.get("sdk_version"); + + assertThat(iosCpu).isEqualTo("i386"); + assertThat(env).containsEntry("APPLE_SDK_PLATFORM", "iPhoneSimulator"); + assertThat(env).containsEntry("APPLE_SDK_VERSION_OVERRIDE", "8.4"); + assertThat(sdkVersion).isEqualTo("8.4"); + assertThat(skylarkTarget.get("xcode_version")).isEqualTo("7.1"); + assertThat(skylarkTarget.get("single_arch_platform")).isEqualTo("IOS_SIMULATOR"); + assertThat(skylarkTarget.get("single_arch_cpu")).isEqualTo("i386"); + assertThat(skylarkTarget.get("platform_type")).isEqualTo("ios"); + assertThat(skylarkTarget.get("bitcode_mode")).isEqualTo("none"); + } + + @Test + public void testSkylarkCanAccessApplePlatformNames() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " platform = ctx.fragments.apple.ios_cpu_platform()", + " return struct(", + " name=platform.name_in_plist,", + " )", + "test_rule = rule(", + "implementation = _test_rule_impl,", + "fragments = ['apple']", + ")"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name='my_target',", + ")"); + + useConfiguration("--cpu=ios_i386"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + Object name = skylarkTarget.get("name"); + assertThat(name).isEqualTo("iPhoneSimulator"); + } + + @Test + public void testSkylarkCanAccessAppleToolchain() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def swift_binary_impl(ctx):", + " apple_toolchain = apple_common.apple_toolchain()", + " sdk_dir = apple_toolchain.sdk_dir()", + " platform_developer_framework_dir = \\", + " apple_toolchain.platform_developer_framework_dir(ctx.fragments.apple)", + " return struct(", + " platform_developer_framework_dir=platform_developer_framework_dir,", + " sdk_dir=sdk_dir,", + " )", + "swift_binary = rule(", + "implementation = swift_binary_impl,", + "fragments = ['apple']", + ")"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'swift_binary')", + "swift_binary(", + " name='my_target',", + ")"); + + useConfiguration("--cpu=ios_i386"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + String platformDevFrameworksDir = + (String) + skylarkTarget.get("platform_developer_framework_dir"); + String sdkDir = (String) skylarkTarget.get("sdk_dir"); + + assertThat(platformDevFrameworksDir) + .isEqualTo( + AppleToolchain.developerDir() + + "/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks"); + assertThat(sdkDir).isEqualTo(AppleToolchain.sdkDir()); + } + + @Test + public void testSkylarkCanAccessSdkAndMinimumOs() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def swift_binary_impl(ctx):", + " ios_sdk_version = ctx.fragments.apple.sdk_version_for_platform\\", + "(apple_common.platform.ios_device)", + " watchos_sdk_version = ctx.fragments.apple.sdk_version_for_platform\\", + "(apple_common.platform.watchos_device)", + " tvos_sdk_version = ctx.fragments.apple.sdk_version_for_platform\\", + "(apple_common.platform.tvos_device)", + " macos_sdk_version = ctx.fragments.apple.sdk_version_for_platform\\", + "(apple_common.platform.macos)", + " ios_minimum_os = ctx.fragments.apple.minimum_os_for_platform_type\\", + "(apple_common.platform_type.ios)", + " watchos_minimum_os = ctx.fragments.apple.minimum_os_for_platform_type\\", + "(apple_common.platform_type.watchos)", + " tvos_minimum_os = ctx.fragments.apple.minimum_os_for_platform_type\\", + "(apple_common.platform_type.tvos)", + " return struct(", + " ios_sdk_version=str(ios_sdk_version),", + " watchos_sdk_version=str(watchos_sdk_version),", + " tvos_sdk_version=str(tvos_sdk_version),", + " macos_sdk_version=str(macos_sdk_version),", + " ios_minimum_os=str(ios_minimum_os),", + " watchos_minimum_os=str(watchos_minimum_os),", + " tvos_minimum_os=str(tvos_minimum_os)", + " )", + "swift_binary = rule(", + "implementation = swift_binary_impl,", + "fragments = ['apple']", + ")"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'swift_binary')", + "swift_binary(", + " name='my_target',", + ")"); + + useConfiguration("--ios_sdk_version=1.1", "--ios_minimum_os=1.0", + "--watchos_sdk_version=2.1", "--watchos_minimum_os=2.0", + "--tvos_sdk_version=3.1", "--tvos_minimum_os=3.0", + "--macos_sdk_version=4.1"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + + assertThat(skylarkTarget.get("ios_sdk_version")).isEqualTo("1.1"); + assertThat(skylarkTarget.get("ios_minimum_os")).isEqualTo("1.0"); + assertThat(skylarkTarget.get("watchos_sdk_version")).isEqualTo("2.1"); + assertThat(skylarkTarget.get("watchos_minimum_os")).isEqualTo("2.0"); + assertThat(skylarkTarget.get("tvos_sdk_version")).isEqualTo("3.1"); + assertThat(skylarkTarget.get("tvos_minimum_os")).isEqualTo("3.0"); + assertThat(skylarkTarget.get("macos_sdk_version")).isEqualTo("4.1"); + + useConfiguration("--ios_sdk_version=1.1", + "--watchos_sdk_version=2.1", + "--tvos_sdk_version=3.1", + "--macos_sdk_version=4.1"); + skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + assertThat(skylarkTarget.get("ios_sdk_version")).isEqualTo("1.1"); + assertThat(skylarkTarget.get("ios_minimum_os")).isEqualTo("1.1"); + assertThat(skylarkTarget.get("watchos_sdk_version")).isEqualTo("2.1"); + assertThat(skylarkTarget.get("watchos_minimum_os")).isEqualTo("2.1"); + assertThat(skylarkTarget.get("tvos_sdk_version")).isEqualTo("3.1"); + assertThat(skylarkTarget.get("tvos_minimum_os")).isEqualTo("3.1"); + assertThat(skylarkTarget.get("macos_sdk_version")).isEqualTo("4.1"); + } + + @Test + public void testSkylarkCanAccessObjcConfiguration() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/objc_rules.bzl", + "def swift_binary_impl(ctx):", + " copts = ctx.fragments.objc.copts", + " compilation_mode_copts = ctx.fragments.objc.copts_for_current_compilation_mode", + " ios_simulator_device = ctx.fragments.objc.ios_simulator_device", + " ios_simulator_version = ctx.fragments.objc.ios_simulator_version", + " signing_certificate_name = ctx.fragments.objc.signing_certificate_name", + " generate_dsym = ctx.fragments.objc.generate_dsym", + " return struct(", + " copts=copts,", + " compilation_mode_copts=compilation_mode_copts,", + " ios_simulator_device=ios_simulator_device,", + " ios_simulator_version=str(ios_simulator_version),", + " signing_certificate_name=signing_certificate_name,", + " generate_dsym=generate_dsym,", + " )", + "swift_binary = rule(", + "implementation = swift_binary_impl,", + "fragments = ['objc']", + ")"); + + scratch.file("examples/objc_skylark/a.m"); + scratch.file( + "examples/objc_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/objc_rules', 'swift_binary')", + "swift_binary(", + " name='my_target',", + ")"); + + useConfiguration( + "--compilation_mode=opt", + "--objccopt=-DTestObjcCopt", + "--ios_simulator_device='iPhone 6'", + "--ios_simulator_version=8.4", + "--ios_signing_cert_name='Apple Developer'", + "--apple_generate_dsym"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/objc_skylark:my_target"); + + @SuppressWarnings("unchecked") + List<String> copts = (List<String>) skylarkTarget.get("copts"); + @SuppressWarnings("unchecked") + List<String> compilationModeCopts = (List<String>) skylarkTarget.get("compilation_mode_copts"); + Object iosSimulatorDevice = skylarkTarget.get("ios_simulator_device"); + Object iosSimulatorVersion = skylarkTarget.get("ios_simulator_version"); + Object signingCertificateName = skylarkTarget.get("signing_certificate_name"); + Boolean generateDsym = (Boolean) skylarkTarget.get("generate_dsym"); + + assertThat(copts).contains("-DTestObjcCopt"); + assertThat(compilationModeCopts).containsExactlyElementsIn(ObjcConfiguration.OPT_COPTS); + assertThat(iosSimulatorDevice).isEqualTo("'iPhone 6'"); + assertThat(iosSimulatorVersion).isEqualTo("8.4"); + assertThat(signingCertificateName).isEqualTo("'Apple Developer'"); + assertThat(generateDsym).isTrue(); + } + + @Test + public void testSigningCertificateNameCanReturnNone() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/objc_rules.bzl", + "def my_rule_impl(ctx):", + " signing_certificate_name = ctx.fragments.objc.signing_certificate_name", + " return struct(", + " signing_certificate_name=str(signing_certificate_name),", + " )", + "my_rule = rule(", + "implementation = my_rule_impl,", + "fragments = ['objc']", + ")"); + + scratch.file("examples/objc_skylark/a.m"); + scratch.file( + "examples/objc_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/objc_rules', 'my_rule')", + "my_rule(", + " name='my_target',", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/objc_skylark:my_target"); + + Object signingCertificateName = skylarkTarget.get("signing_certificate_name"); + assertThat(signingCertificateName).isEqualTo("None"); + } + + @Test + public void testUsesDebugEntitlementsIsTrueIfCompilationModeIsNotOpt() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/objc_rules.bzl", + "def test_rule_impl(ctx):", + " uses_device_debug_entitlements = ctx.fragments.objc.uses_device_debug_entitlements", + " return struct(", + " uses_device_debug_entitlements=uses_device_debug_entitlements,", + " )", + "test_rule = rule(", + "implementation = test_rule_impl,", + "fragments = ['objc']", + ")"); + + scratch.file("examples/objc_skylark/a.m"); + scratch.file( + "examples/objc_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/objc_rules', 'test_rule')", + "test_rule(", + " name='my_target',", + ")"); + + useConfiguration("--compilation_mode=dbg"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/objc_skylark:my_target"); + + boolean usesDeviceDebugEntitlements = (boolean) skylarkTarget + .get("uses_device_debug_entitlements"); + assertThat(usesDeviceDebugEntitlements).isTrue(); + } + + @Test + public void testUsesDebugEntitlementsIsFalseIfFlagIsExplicitlyFalse() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/objc_rules.bzl", + "def test_rule_impl(ctx):", + " uses_device_debug_entitlements = ctx.fragments.objc.uses_device_debug_entitlements", + " return struct(", + " uses_device_debug_entitlements=uses_device_debug_entitlements,", + " )", + "test_rule = rule(", + "implementation = test_rule_impl,", + "fragments = ['objc']", + ")"); + + scratch.file("examples/objc_skylark/a.m"); + scratch.file( + "examples/objc_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/objc_rules', 'test_rule')", + "test_rule(", + " name='my_target',", + ")"); + + useConfiguration( + "--compilation_mode=dbg", + "--nodevice_debug_entitlements"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/objc_skylark:my_target"); + + boolean usesDeviceDebugEntitlements = (boolean) skylarkTarget + .get("uses_device_debug_entitlements"); + assertThat(usesDeviceDebugEntitlements).isFalse(); + } + + @Test + public void testUsesDebugEntitlementsIsFalseIfCompilationModeIsOpt() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/objc_rules.bzl", + "def test_rule_impl(ctx):", + " uses_device_debug_entitlements = ctx.fragments.objc.uses_device_debug_entitlements", + " return struct(", + " uses_device_debug_entitlements=uses_device_debug_entitlements,", + " )", + "test_rule = rule(", + "implementation = test_rule_impl,", + "fragments = ['objc']", + ")"); + + scratch.file("examples/objc_skylark/a.m"); + scratch.file( + "examples/objc_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/objc_rules', 'test_rule')", + "test_rule(", + " name='my_target',", + ")"); + + useConfiguration("--compilation_mode=opt"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/objc_skylark:my_target"); + + boolean usesDeviceDebugEntitlements = (boolean) skylarkTarget + .get("uses_device_debug_entitlements"); + assertThat(usesDeviceDebugEntitlements).isFalse(); + } + + private ConfiguredTarget createObjcProviderSkylarkTarget(String... implLines) throws Exception { + String[] impl = + ObjectArrays.concat( + ObjectArrays.concat("def swift_binary_impl(ctx):", implLines), + new String[] { + "swift_binary = rule(", + "implementation = swift_binary_impl,", + "attrs = {", + " 'deps': attr.label_list(", + "allow_files = False, mandatory = False, providers = ['objc'])", + "})" + }, + String.class); + + scratch.file("examples/rule/BUILD"); + scratch.file("examples/rule/objc_rules.bzl", impl); + scratch.file( + "examples/objc_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/objc_rules', 'swift_binary')", + "swift_binary(", + " name='my_target',", + " deps=[':lib'],", + ")", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + " defines = ['define_from_dep']", + ")"); + + return getConfiguredTarget("//examples/objc_skylark:my_target"); + } + + @Test + public void testSkylarkCanCreateObjcProviderFromScratch() throws Exception { + ConfiguredTarget skylarkTarget = + createObjcProviderSkylarkTarget( + " defines = depset(['define1', 'define2'])", + " linkopts = depset(['somelinkopt'])", + " created_provider = apple_common.new_objc_provider\\", + "(define=defines, linkopt=linkopts)", + " return struct(objc=created_provider)"); + + Iterable<String> foundLinkopts = + skylarkTarget.getProvider(ObjcProvider.class).get(ObjcProvider.LINKOPT); + Iterable<String> foundDefines = + skylarkTarget.getProvider(ObjcProvider.class).get(ObjcProvider.DEFINE); + boolean usesSwift = + skylarkTarget.getProvider(ObjcProvider.class).is(ObjcProvider.Flag.USES_SWIFT); + + assertThat(foundLinkopts).containsExactly("somelinkopt"); + assertThat(foundDefines).containsExactly("define1", "define2"); + assertThat(usesSwift).isFalse(); + } + + @Test + public void testSkylarkCanPassLinkInputsInObjcProvider() throws Exception { + ConfiguredTarget skylarkTarget = + createObjcProviderSkylarkTarget( + " file = ctx.actions.declare_file('foo.ast')", + " ctx.actions.run_shell(outputs=[file], command='echo')", + " link_inputs = depset([file])", + " created_provider = apple_common.new_objc_provider\\", + "(link_inputs=link_inputs)", + " return struct(objc=created_provider)"); + + Iterable<Artifact> foundLinkInputs = + skylarkTarget.getProvider(ObjcProvider.class).get(ObjcProvider.LINK_INPUTS); + assertThat(ActionsTestUtil.baseArtifactNames(foundLinkInputs)).contains("foo.ast"); + } + + @Test + public void testSkylarkCanPassUsesSwiftFlag() throws Exception { + ConfiguredTarget skylarkTarget = + createObjcProviderSkylarkTarget( + " created_provider = apple_common.new_objc_provider(uses_swift=True)", + " return struct(objc=created_provider)"); + + boolean usesSwift = + skylarkTarget.getProvider(ObjcProvider.class).is(ObjcProvider.Flag.USES_SWIFT); + + assertThat(usesSwift).isTrue(); + } + + @Test + public void testSkylarkCanCreateObjcProviderWithPathFragments() throws Exception { + ConfiguredTarget skylarkTarget = + createObjcProviderSkylarkTarget( + " includes = depset(['path1', 'path_dir/path2', 'path_dir1/path_dir2/path3'])", + " created_provider = apple_common.new_objc_provider\\", + "(include=includes)", + " return struct(objc=created_provider)"); + + Iterable<PathFragment> foundIncludes = + skylarkTarget.getProvider(ObjcProvider.class).get(ObjcProvider.INCLUDE); + + assertThat(foundIncludes) + .containsExactly( + PathFragment.create("path1"), + PathFragment.create("path_dir/path2"), + PathFragment.create("path_dir1/path_dir2/path3")); + } + + @Test + public void testSkylarkCanCreateObjcProviderWithStrictDeps() throws Exception { + ConfiguredTarget skylarkTarget = + createObjcProviderSkylarkTarget( + " strict_includes = depset(['path1'])", + " propagated_includes = depset(['path2'])", + " strict_provider = apple_common.new_objc_provider\\", + "(include=strict_includes)", + " created_provider = apple_common.new_objc_provider\\", + "(include=propagated_includes, direct_dep_providers=[strict_provider])", + " return struct(objc=created_provider)"); + + ObjcProvider skylarkProvider = skylarkTarget.getProvider(ObjcProvider.class); + ObjcProvider skylarkProviderDirectDepender = + new ObjcProvider.Builder().addTransitiveAndPropagate(skylarkProvider).build(); + ObjcProvider skylarkProviderIndirectDepender = + new ObjcProvider.Builder().addTransitiveAndPropagate(skylarkProviderDirectDepender).build(); + + assertThat(skylarkProvider.get(ObjcProvider.INCLUDE)) + .containsExactly(PathFragment.create("path1"), PathFragment.create("path2")); + assertThat(skylarkProviderDirectDepender.get(ObjcProvider.INCLUDE)) + .containsExactly(PathFragment.create("path1"), PathFragment.create("path2")); + assertThat(skylarkProviderIndirectDepender.get(ObjcProvider.INCLUDE)) + .containsExactly(PathFragment.create("path2")); + } + + @Test + public void testSkylarkCanCreateObjcProviderFromObjcProvider() throws Exception { + ConfiguredTarget skylarkTarget = + createObjcProviderSkylarkTarget( + " dep = ctx.attr.deps[0]", + " define = depset(['define_from_impl'])", + " created_provider = apple_common.new_objc_provider\\", + "(providers=[dep.objc], define=define)", + " return struct(objc=created_provider)"); + + Iterable<String> foundStrings = + skylarkTarget.getProvider(ObjcProvider.class).get(ObjcProvider.DEFINE); + + assertThat(foundStrings).containsExactly("define_from_dep", "define_from_impl"); + } + + @Test + public void testSkylarkErrorOnBadObjcProviderInputKey() throws Exception { + try { + createObjcProviderSkylarkTarget( + " created_provider = apple_common.new_objc_provider(foo=depset(['bar']))", + " return struct(objc=created_provider)"); + fail("Should throw AssertionError"); + } catch (AssertionError e) { + assertThat(e) + .hasMessageThat() + .contains(String.format(AppleSkylarkCommon.BAD_KEY_ERROR, "foo")); + } + } + + @Test + public void testSkylarkErrorOnNonSetObjcProviderInputValue() throws Exception { + try { + createObjcProviderSkylarkTarget( + " created_provider = apple_common.new_objc_provider(library='bar')", + " return struct(objc=created_provider)"); + fail("Should throw AssertionError"); + } catch (AssertionError e) { + assertThat(e) + .hasMessageThat() + .contains(String.format(AppleSkylarkCommon.NOT_SET_ERROR, "library", "string")); + } + } + + @Test + public void testSkylarkErrorOnObjcProviderInputValueWrongSetType() throws Exception { + try { + createObjcProviderSkylarkTarget( + " created_provider = apple_common.new_objc_provider(library=depset(['bar']))", + " return struct(objc=created_provider)"); + fail("Should throw AssertionError"); + } catch (AssertionError e) { + assertThat(e) + .hasMessageThat() + .contains( + String.format(AppleSkylarkCommon.BAD_SET_TYPE_ERROR, "library", "File", "string")); + } + } + + @Test + public void testSkylarkErrorOnNonIterableObjcProviderProviderValue() throws Exception { + try { + createObjcProviderSkylarkTarget( + " created_provider = apple_common.new_objc_provider(providers='bar')", + " return struct(objc=created_provider)"); + fail("Should throw AssertionError"); + } catch (AssertionError e) { + assertThat(e) + .hasMessageThat() + .contains(String.format(AppleSkylarkCommon.BAD_PROVIDERS_ITER_ERROR, "string")); + } + } + + @Test + public void testSkylarkErrorOnBadIterableObjcProviderProviderValue() throws Exception { + try { + createObjcProviderSkylarkTarget( + " created_provider = apple_common.new_objc_provider(providers=['bar'])", + " return struct(objc=created_provider)"); + fail("Should throw AssertionError"); + } catch (AssertionError e) { + assertThat(e) + .hasMessageThat() + .contains(String.format(AppleSkylarkCommon.BAD_PROVIDERS_ELEM_ERROR, "string")); + } + } + + @Test + public void testEmptyObjcProviderKeysArePresent() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def swift_binary_impl(ctx):", + " objc_provider = ctx.attr.deps[0].objc", + " return struct(", + " empty_value=objc_provider.include,", + " )", + "swift_binary = rule(", + "implementation = swift_binary_impl,", + "fragments = ['apple'],", + "attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False, providers = ['objc'])", + "})"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'swift_binary')", + "swift_binary(", + " name='my_target',", + " deps=[':lib'],", + ")", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + ")"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + SkylarkNestedSet emptyValue = + (SkylarkNestedSet) + skylarkTarget.get("empty_value"); + assertThat(emptyValue.toCollection()).isEmpty(); + } + + @Test + public void testSkylarkCanAccessProvidedBundleFiles() throws Exception { + // Since the collections of structs with Artifact values are extremely difficult to test with + // Truth, we fudge them in the Skylark side to return easily comparable dictionaries instead. + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _simplify_bundle_file(bf):", + " return {'file': bf.file.path, 'bundle_path': bf.bundle_path}", + "def _test_rule_impl(ctx):", + " dep = ctx.attr.deps[0]", + " objc_provider = dep.objc", + " bundle_file = [_simplify_bundle_file(bf) for bf in list(objc_provider.bundle_file)]", + " return struct(", + " bundle_file=bundle_file,", + " )", + "test_rule = rule(implementation = _test_rule_impl,", + " attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False, providers = ['objc'])", + "})"); + + scratch.file("examples/apple_skylark/a.m"); + scratch.file("examples/apple_skylark/flattened/a/a.txt"); + scratch.file("examples/apple_skylark/flattened/b.lproj/b.txt"); + scratch.file("examples/apple_skylark/structured/c/c.txt"); + scratch.file("examples/apple_skylark/structured/d/d.txt"); + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + " resources = glob(['flattened/**']),", + " structured_resources = glob(['structured/**']),", + ")", + "test_rule(", + " name = 'my_target',", + " deps = [':lib'],", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + Iterable<?> bundleFiles = (Iterable<?>) + skylarkTarget.get("bundle_file"); + assertThat(bundleFiles).containsAllOf(ImmutableMap.of( + BUNDLE_PATH_FIELD, "a.txt", + BUNDLED_FIELD, "examples/apple_skylark/flattened/a/a.txt" + ), ImmutableMap.of( + BUNDLE_PATH_FIELD, "b.lproj/b.txt", + BUNDLED_FIELD, "examples/apple_skylark/flattened/b.lproj/b.txt" + ), ImmutableMap.of( + BUNDLE_PATH_FIELD, "structured/c/c.txt", + BUNDLED_FIELD, "examples/apple_skylark/structured/c/c.txt" + ), ImmutableMap.of( + BUNDLE_PATH_FIELD, "structured/d/d.txt", + BUNDLED_FIELD, "examples/apple_skylark/structured/d/d.txt" + )); + } + + @Test + public void testSkylarkCanAccessSdkFrameworks() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " dep = ctx.attr.deps[0]", + " objc_provider = dep.objc", + " return struct(", + " sdk_frameworks=objc_provider.sdk_framework,", + " )", + "test_rule = rule(implementation = _test_rule_impl,", + " attrs = {", + " 'deps': attr.label_list(allow_files = False, mandatory = False, providers = ['objc'])", + "})"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "objc_library(", + " name = 'lib',", + " srcs = ['a.m'],", + " sdk_frameworks = ['Accelerate', 'GLKit'],", + ")", + "test_rule(", + " name = 'my_target',", + " deps = [':lib'],", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + SkylarkNestedSet sdkFrameworks = (SkylarkNestedSet) + skylarkTarget.get("sdk_frameworks"); + assertThat(sdkFrameworks.toCollection()).containsAllOf("Accelerate", "GLKit"); + } + + @Test + public void testSkylarkCanAccessAndUseApplePlatformTypes() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " apple = ctx.fragments.apple", + " ios_platform = apple.multi_arch_platform(apple_common.platform_type.ios)", + " watchos_platform = apple.multi_arch_platform(apple_common.platform_type.watchos)", + " tvos_platform = apple.multi_arch_platform(apple_common.platform_type.tvos)", + " return struct(", + " ios_platform=str(ios_platform),", + " watchos_platform=str(watchos_platform),", + " tvos_platform=str(tvos_platform),", + " )", + "test_rule = rule(implementation = _test_rule_impl,", + " fragments = ['apple'])"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + useConfiguration( + "--ios_multi_cpus=arm64,armv7", + "--watchos_cpus=armv7k", + "--tvos_cpus=arm64"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + Object iosPlatform = + skylarkTarget.get("ios_platform"); + Object watchosPlatform = + skylarkTarget.get("watchos_platform"); + Object tvosPlatform = + skylarkTarget.get("tvos_platform"); + + assertThat(iosPlatform).isEqualTo("IOS_DEVICE"); + assertThat(watchosPlatform).isEqualTo("WATCHOS_DEVICE"); + assertThat(tvosPlatform).isEqualTo("TVOS_DEVICE"); + } + + @Test + public void testPlatformIsDeviceReturnsTrueForDevicePlatforms() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " apple = ctx.fragments.apple", + " platform = apple.multi_arch_platform(apple_common.platform_type.ios)", + " return struct(", + " is_device=platform.is_device,", + " )", + "test_rule = rule(implementation = _test_rule_impl,", + " fragments = ['apple'])"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + useConfiguration( + "--ios_multi_cpus=arm64,armv7"); + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + Boolean isDevice = + (Boolean) skylarkTarget.get("is_device"); + assertThat(isDevice).isTrue(); + } + + @Test + public void testPlatformIsDeviceReturnsFalseForSimulatorPlatforms() throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " apple = ctx.fragments.apple", + " platform = apple.multi_arch_platform(apple_common.platform_type.ios)", + " return struct(", + " is_device=platform.is_device,", + " )", + "test_rule = rule(implementation = _test_rule_impl,", + " fragments = ['apple'])"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + Boolean isDevice = + (Boolean) skylarkTarget.get("is_device"); + assertThat(isDevice).isFalse(); + } + + @Test + public void testXcTestAppProviderCanBeCreated() throws Exception { + scratch.file("examples/rule/BUILD", + "exports_files(['test_artifact'])"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " artifact = list(ctx.attr.test_artifact.files)[0]", + " objc_provider = apple_common.new_objc_provider(define=depset(['TEST_DEFINE']))", + " xctest_app_provider = apple_common.new_xctest_app_provider(", + " bundle_loader=artifact, ipa=artifact, objc_provider=objc_provider)", + " return struct(", + " xctest_app=xctest_app_provider,", + " )", + "test_rule = rule(implementation = _test_rule_impl,", + " attrs = {", + " 'test_artifact': attr.label(", + " allow_single_file=True,", + " default=Label('//examples/rule:test_artifact')),", + " })"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + XcTestAppProvider xcTestAppProvider = skylarkTarget.getProvider(XcTestAppProvider.class); + + assertThat(xcTestAppProvider.getBundleLoader().getRootRelativePathString()) + .isEqualTo("examples/rule/test_artifact"); + assertThat(xcTestAppProvider.getIpa().getRootRelativePathString()) + .isEqualTo("examples/rule/test_artifact"); + assertThat(xcTestAppProvider.getObjcProvider().get(ObjcProvider.DEFINE)) + .containsExactly("TEST_DEFINE"); + } + + @Test + public void testSkylarkWithRunMemleaksEnabled() throws Exception { + useConfiguration("--ios_memleaks"); + checkSkylarkRunMemleaksWithExpectedValue(true); + } + + @Test + public void testSkylarkWithRunMemleaksDisabled() throws Exception { + checkSkylarkRunMemleaksWithExpectedValue(false); + } + + @Test + public void testDottedVersion() throws Exception { + scratch.file("examples/rule/BUILD", + "exports_files(['test_artifact'])"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " version = apple_common.dotted_version('5.4')", + " return struct(", + " version=version", + " )", + "test_rule = rule(implementation = _test_rule_impl)"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + DottedVersion version = (DottedVersion) skylarkTarget.get("version"); + assertThat(version).isEqualTo(DottedVersion.fromString("5.4")); + } + + @Test + public void testDottedVersion_invalid() throws Exception { + scratch.file("examples/rule/BUILD", + "exports_files(['test_artifact'])"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " version = apple_common.dotted_version('hello')", + " return struct(", + " version=version", + " )", + "test_rule = rule(implementation = _test_rule_impl)"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + try { + getConfiguredTarget("//examples/apple_skylark:my_target"); + fail("Expected an error to be thrown for invalid dotted version string"); + } catch (AssertionError e) { + assertThat(e).hasMessageThat().contains("illegal argument in call to dotted_version"); + assertThat(e).hasMessageThat().contains("Dotted version components must all be of the form"); + } + } + + private void checkSkylarkRunMemleaksWithExpectedValue(boolean expectedValue) throws Exception { + scratch.file("examples/rule/BUILD"); + scratch.file( + "examples/rule/apple_rules.bzl", + "def _test_rule_impl(ctx):", + " return struct(run_memleaks = ctx.fragments.objc.run_memleaks)", + "test_rule = rule(implementation = _test_rule_impl,", + " fragments = ['objc'],", + " attrs = {},", + ")"); + + scratch.file( + "examples/apple_skylark/BUILD", + "package(default_visibility = ['//visibility:public'])", + "load('/examples/rule/apple_rules', 'test_rule')", + "test_rule(", + " name = 'my_target',", + ")"); + + ConfiguredTarget skylarkTarget = getConfiguredTarget("//examples/apple_skylark:my_target"); + + boolean runMemleaks = (boolean) skylarkTarget.get("run_memleaks"); + assertThat(runMemleaks).isEqualTo(expectedValue); + } +} |