Skip to content

Commit

Permalink
Add a Skylark rule to build Swift modules.
Browse files Browse the repository at this point in the history
* Adds a swift_library rule that produces a (.a, .swiftmodule) pair. It can handle dependencies between modules and can be used as a dependency of objc_binary.
* Does not work with Objective-C yet.

--
MOS_MIGRATED_REVID=120578875
  • Loading branch information
Dmitry Shevchenko authored and damienmg committed Apr 22, 2016
1 parent e7be839 commit 2036dbd
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ filegroup(
"//examples/py:srcs",
"//examples/py_native:srcs",
"//examples/shell:srcs",
"//examples/swift:srcs",
],
)
19 changes: 19 additions & 0 deletions examples/swift/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package(default_visibility = ["//visibility:public"])

licenses(["notice"]) # Apache 2.0

load("//tools/build_defs/apple:swift.bzl", "swift_library")

swift_library(
name = "swift_lib",
srcs = glob(["*.swift"]),
deps = ["//examples/swift/BarLib"],
)

filegroup(
name = "srcs",
srcs = glob(["**"]) + [
"//examples/swift/BarLib:srcs",
],
visibility = ["//examples:__pkg__"],
)
15 changes: 15 additions & 0 deletions examples/swift/BarLib/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package(default_visibility = ["//visibility:public"])

licenses(["notice"]) # Apache 2.0

load("//tools/build_defs/apple:swift.bzl", "swift_library")

swift_library(
name = "BarLib",
srcs = ["mul.swift"],
)

filegroup(
name = "srcs",
srcs = glob(["**"]),
)
8 changes: 8 additions & 0 deletions examples/swift/BarLib/mul.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// Class used to multiply stuff.
public class Multiplier {
public init() {}

public func multiply(a: Int, _ b: Int) -> Int {
return a * b
}
}
4 changes: 4 additions & 0 deletions examples/swift/constants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Constants {
static var x = 2
static var y = 3
}
9 changes: 9 additions & 0 deletions examples/swift/foo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import class BarLib.Multiplier

public class Foo {
public init() {}

public func multiply() -> Int {
return Multiplier().multiply(Constants.x, Constants.y)
}
}
10 changes: 10 additions & 0 deletions src/test/shell/bazel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ sh_test(
data = [":test-deps"],
)

sh_test(
name = "bazel_apple_test",
srcs = ["bazel_apple_test.sh"],
data = [
":objc-deps",
":test-deps",
"//:workspace-file",
],
)

sh_test(
name = "bazel_rules_test",
size = "large",
Expand Down
42 changes: 42 additions & 0 deletions src/test/shell/bazel/bazel_apple_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/bash
#
# 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.
#
# Tests the examples provided in Bazel
#

# Load test environment
source $(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-setup.sh \
|| { echo "test-setup.sh not found!" >&2; exit 1; }

if [ "${PLATFORM}" != "darwin" ]; then
echo "This test suite requires running on OS X" >&2
exit 0
fi

function set_up() {
copy_examples
}

function test_swift_library() {
rm WORKSPACE
ln -sv ${workspace_file} WORKSPACE

local swift_lib_pkg=examples/swift
assert_build_output ./bazel-bin/${swift_lib_pkg}/swift_lib.a ${swift_lib_pkg}:swift_lib
assert_build_output ./bazel-bin/${swift_lib_pkg}/swift_lib.swiftmodule ${swift_lib_pkg}:swift_lib
}

run_suite "apple_tests"
2 changes: 2 additions & 0 deletions tools/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ filegroup(
"//tools/android:srcs",
"//tools/android/jack:srcs",
"//tools/buildstamp:srcs",
"//tools/build_defs/apple:srcs",
"//tools/build_defs/docker:srcs",
"//tools/build_defs/jsonnet:srcs",
"//tools/build_defs/pkg:srcs",
Expand All @@ -34,6 +35,7 @@ filegroup(
srcs = glob(["**"]) + [
"//tools/android/jack:srcs",
"//tools/android:srcs",
"//tools/build_defs/apple:srcs",
"//tools/build_defs/docker:srcs",
"//tools/build_defs/jsonnet:srcs",
"//tools/build_defs/pkg:srcs",
Expand Down
19 changes: 19 additions & 0 deletions tools/build_defs/apple/shared.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ DARWIN_EXECUTION_REQUIREMENTS = {"requires-darwin": ""}
See :func:`apple_action`."""

XCRUNWRAPPER_LABEL = "//external:xcrunwrapper"
"""The label for xcrunwrapper tool."""

def apple_action(ctx, **kw):
"""Creates an action that only runs on MacOS/Darwin.
Expand All @@ -49,3 +52,19 @@ def apple_action(ctx, **kw):

ctx.action(**kw)

def xcrun_action(ctx, **kw):
"""Creates an apple action that executes xcrunwrapper.
args:
ctx: The context of the rule that owns this action.
This method takes the same keyword arguments as ctx.action, however you don't
need to specify the executable.
"""
platform = ctx.fragments.apple.ios_cpu_platform()
action_env = ctx.fragments.apple.target_apple_env(platform) \
+ ctx.fragments.apple.apple_host_system_env()
env = kw.get('env', {})
kw['env'] = env + action_env

apple_action(ctx, executable=ctx.executable._xcrunwrapper, **kw)
110 changes: 110 additions & 0 deletions tools/build_defs/apple/swift.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 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.

"""Skylark rules for Swift."""

load("shared", "xcrun_action", "XCRUNWRAPPER_LABEL")

def _swift_target(cpu, sdk_version):
"""Returns a target triplet for Swift compiler."""
return "%s-apple-ios%s" % (cpu, sdk_version)

def _swift_library_impl(ctx):
"""Implementation for swift_library Skylark rule."""
cpu = ctx.fragments.apple.ios_cpu()
platform = ctx.fragments.apple.ios_cpu_platform()
sdk_version = ctx.fragments.apple.sdk_version_for_platform(platform)
target = _swift_target(cpu, sdk_version)

# Collect transitive dependecies.
dep_modules = []
dep_libs = []
for x in ctx.attr.deps:
swift_provider = x.swift
dep_libs.append(swift_provider.library)
dep_libs += swift_provider.transitive_libs

dep_modules.append(swift_provider.module)
dep_modules += swift_provider.transitive_modules

# TODO(b/28005753): Currently this is not really a library, but an object
# file, does not matter to the linker, but should be replaced with proper ar
# call.
output_lib = ctx.outputs.swift_lib
output_module = ctx.outputs.swift_module

srcs_args = [f.path for f in ctx.files.srcs]

# TODO(b/28005582): Instead of including a dir for each dependecy, output to
# a shared dir and include that?
include_dirs = set([x.dirname for x in dep_modules])
include_args = ["-I%s" % d for d in include_dirs]

args = [
"swift",
"-frontend",
"-emit-object",
"-emit-module-path", output_module.path,
"-module-name", ctx.label.name,
"-parse-as-library",
"-target", target,
# TODO(b/28049126): Replace this value with apple_toolchain call.
"-sdk", "__BAZEL_XCODE_SDKROOT__",
"-o", output_lib.path,
] + srcs_args + include_args

xcrun_action(ctx,
inputs = ctx.files.srcs + dep_modules + dep_libs,
outputs = (output_lib, output_module),
mnemonic = 'SwiftCompile',
arguments = args,
use_default_shell_env = False,
progress_message = ("Compiling Swift module %s (%d files)"
% (ctx.label.name, len(ctx.files.srcs))))

return struct(
swift=struct(
library=output_lib,
module=output_module,
transitive_libs=dep_libs,
transitive_modules=dep_modules),
objc_export=struct(
library=set([output_lib] + dep_libs),
)
)

swift_library = rule(
_swift_library_impl,
attrs = {
"srcs": attr.label_list(allow_files = FileType([".swift"])),
"deps": attr.label_list(providers=["swift"]),
"_xcrunwrapper": attr.label(
executable=True,
default=Label(XCRUNWRAPPER_LABEL))},
fragments = ["apple"],
outputs = {
"swift_lib": "%{name}.a",
"swift_module": "%{name}.swiftmodule",
},
)
"""
Builds a Swift module.
A module is a pair of static library (.a) + module header (.swiftmodule).
Dependant targets can import this module as "import RuleName".
Args:
srcs: Swift sources that comprise this module.
deps: Other Swift modules.
"""

0 comments on commit 2036dbd

Please sign in to comment.