From 1da1fb9e572a1af341ca92c79c10463131e8ab81 Mon Sep 17 00:00:00 2001 From: Chuck Grindel Date: Fri, 15 Oct 2021 14:40:59 -0600 Subject: [PATCH] Added documentation and updated README with 0.1.0 release info (#16) Updated README.md with the 0.1.0 release info. Added doc describing how the rules and macros work. Added doc describing options for integrating rules_swiftformat into a project. --- .github/workflows/bazel.yml | 2 +- README.md | 92 +++++--------- doc/README.md | 13 +- doc/how_it_works.md | 65 ++++++++++ doc/integrate_with_rules_swift.md | 118 ++++++++++++++++++ examples/README.md | 7 ++ .../.swiftformat | 0 .../BUILD.bazel | 0 examples/rules_swift_helpers/README.md | 7 ++ .../Sources/App/BUILD.bazel | 0 .../Sources/App/main.swift | 0 .../Sources/Foo/BUILD.bazel | 0 .../Sources/Foo/Message.swift | 0 .../Tests/AppTests/BUILD.bazel | 0 .../Tests/AppTests/simple_test.sh | 0 .../Tests/FooTests/BUILD.bazel | 0 .../Tests/FooTests/MessageTests.swift | 0 .../WORKSPACE | 2 +- examples/simple/README.md | 3 + examples/swift_rule_helpers/README.md | 4 - 20 files changed, 246 insertions(+), 67 deletions(-) create mode 100644 doc/how_it_works.md create mode 100644 doc/integrate_with_rules_swift.md create mode 100644 examples/README.md rename examples/{swift_rule_helpers => rules_swift_helpers}/.swiftformat (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/BUILD.bazel (100%) create mode 100644 examples/rules_swift_helpers/README.md rename examples/{swift_rule_helpers => rules_swift_helpers}/Sources/App/BUILD.bazel (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Sources/App/main.swift (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Sources/Foo/BUILD.bazel (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Sources/Foo/Message.swift (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Tests/AppTests/BUILD.bazel (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Tests/AppTests/simple_test.sh (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Tests/FooTests/BUILD.bazel (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/Tests/FooTests/MessageTests.swift (100%) rename examples/{swift_rule_helpers => rules_swift_helpers}/WORKSPACE (93%) create mode 100644 examples/simple/README.md delete mode 100644 examples/swift_rule_helpers/README.md diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index ddbe8cd..cac67fe 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -57,5 +57,5 @@ jobs: - name: Test rules_swift Helpers Example shell: bash run: | - cd examples/swift_rule_helpers + cd examples/rules_swift_helpers bazelisk test //... diff --git a/README.md b/README.md index 01695bc..8df0cae 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,11 @@ This repository contains Bazel rules and macros that will format Swift source fi [nicklockwood/SwiftFormat](https://github.com/nicklockwood/SwiftFormat), test that the formatted files exist in the workspace directory, and copy the formatted files to the workspace directory. -## Reference Documentation - -[Click here](/doc) for reference documentation for the rules and other definitions in this -repository. - ## Quickstart The following provides a quick introduction on how to use the rules in this repository. Also, check -out the [examples directory](examples/) for more information. +out [the documentation](/doc/) and [the examples](/examples/) for more information. ### 1. Configure your workspace to use `rules_swiftformat` @@ -25,10 +20,11 @@ Add the following to your `WORKSPACE` file to add this repository and its depend # Download and configure rules_swiftformat. -# GH008 Update this section once release 0.1.0 is available. -local_repository( +http_archive( name = "cgrindel_rules_swiftformat", - path = "../..", + sha256 = "4942ca4f8f88d926964c7ff9c449c5c7eb2e0f1059675d16f75ea57bfdebb504", + strip_prefix = "rules_swiftformat-0.1.0", + urls = ["https://github.com/cgrindel/rules_swiftformat/archive/v0.1.0.tar.gz"], ) load("@cgrindel_rules_swiftformat//swiftformat:deps.bzl", "swiftformat_rules_dependencies") @@ -68,34 +64,30 @@ swiftformat_load_package() ### 2. Update the `BUILD.bazel` at the root of your workspace -At the root of your workspace, create a `BUILD.bazel` file, if you don't have one. Add the following -load statement. +At the root of your workspace, create a `BUILD.bazel` file, if you don't have one. Add the +following: ```python load( "@cgrindel_rules_swiftformat//swiftformat:swiftformat.bzl", "swiftformat_update_all", ) -``` -Define a target for your [SwiftFormat configuration file -(`.swiftformat`)](https://github.com/nicklockwood/SwiftFormat#config-file). - -```python # We export this file to make it available to other Bazel packages in the workspace. exports_files([".swiftformat"]) -``` - -Next, add the following: -```python +# Define a runnable target to copy all of the formatted files to the workspace directory. swiftformat_update_all( name = "update_all", ) ``` -The [`swiftformat_update_all`](/doc/rules_and_macros_overview.md#swiftformat_update_all) macro will -define a runnable target that will copy all of the formatted Swift source files to the workspace +The `exports_files` declaration defines a target for your [SwiftFormat configuration file +(`.swiftformat`)](https://github.com/nicklockwood/SwiftFormat#config-file). It is referenced by the +`swiftformat_pkg` that we will add to each of the Bazel packages that contain Swift source files. + +The [`swiftformat_update_all`](/doc/rules_and_macros_overview.md#swiftformat_update_all) macro +defines a runnable target that copies all of the formatted Swift source files to the workspace directory. @@ -111,52 +103,34 @@ load( ) swiftformat_pkg( - name = "format", + name = "swiftformat", ) ``` -The `swiftformat_pkg` macro defines targets for a Bazel package which will format the Swift source -files, test that the formatted files are in the workspace directory and copies the formatted files -to the workspace directory. - -## How it works - Format, Test, and Update +The [`swiftformat_pkg`](/doc/rules_and_macros_overview.md#swiftformat_pkg) macro defines targets for +a Bazel package which will format the Swift source files, test that the formatted files are in the +workspace directory and copies the formatted files to the workspace directory. -### Format +### 4. Format, Update, and Test -The Swift source files will be formatted whenever `bazel build` or `bazel test` are invoked in a -package that has a `swiftformat_pkg` declaration. However, the formatted files are not copied to -the workspace directory at this point. They only exist in Bazel's output directories. - -### Test - -The tests that `rules_swiftformat` defines will compare the formatted files in the output directory -to the files in the workspace/source directory. If they do not match, the test fails. As you might -expect, the tests only run when `bazel test` is invoked for a Bazel package. - -### Update - -Each Bazel package that has a `swiftformat_pkg` declaration will contain runnable target that will -copy the formatted source file from the output directory to the workspace/source directory. - -For instance, if you defined your `swiftformat_pkg` with the name `format`, the runnable target is -called `format_update`. So, to update the files in a single package you would run: - -```sh -$ bazel run //path/to/pkg:format_update -``` - -Since finding and running these commands manually would be tedious, we added a -`swiftformat_update_all` declaration at the root of the workspace. This will find all of the update -commands and run them. +From the command-line, you can format the Swift source files, copy them back to the workspace +directory and execute the tests that ensure the formatted soures are in the workspace directory. ```sh +# Format the Swift source files and copy the formatted files back to the workspace directory $ bazel run //:update_all + +# Execute all of your tests including the formatting checks +$ bazel test //... ``` -### Putting it all together +## Learn More -So, the workflow goes something like this: +- [How It Works](/doc/how_it_works.md) +- [How to seamlessly build and format your Swift source + code](/doc/integrate_with_rules_swift.md) using +[swiftformat_library](/doc/rules_and_macros_overview.md#swiftformat_library), +[swiftformat_binary](/doc/rules_and_macros_overview.md#swiftformat_binary), and +[swiftformat_test](/doc/rules_and_macros_overview.md#swiftformat_test). +- Check out the [rest of the documentation](/doc) -1. Make a code change. -2. Build and test: `bazel test //...`. -3. If a format test fails, copy the formatted files: `bazel run //:update_all`. diff --git a/doc/README.md b/doc/README.md index 176ff41..bea2ed8 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,7 +1,16 @@ # Documentation for `rules_swiftformat` -For information on how to use the rules and macros in this repository, check out [the -quickstart](/README.md#quickstart). +## Explainers and How-tos + +- [Quickstart](/README.md#quickstart) - Information on how to use the rules and macros in this repository. +- [How It Works](/doc/how_it_works.md) - Describes how `rules_swiftformat` works. +- [How to Integrate `rules_swiftformat` with `rules_swift`](/doc/integrate_with_rules_swift.md) - + Demonstrates how to seamlessly build and format your Swift sources using +[`swiftformat_library`](/doc/rules_and_macros_overview.md#swiftformat_library), +[`swiftformat_binary`](/doc/rules_and_macros_overview.md#swiftformat_binary), and +[`swiftformat_test`](/doc/rules_and_macros_overview.md#swiftformat_test). + +## Reference Documentation - [Rules and Macros](/doc/rules_and_macros_overview.md) - [Providers](/doc/providers_overview.md) diff --git a/doc/how_it_works.md b/doc/how_it_works.md new file mode 100644 index 0000000..e56f411 --- /dev/null +++ b/doc/how_it_works.md @@ -0,0 +1,65 @@ +# How It Works - Format, Test, and Update + +This document describes how `rules_swiftformat` works, helping you maintain your project's code +hygiene. + +## Capabilities + +The rules and macros in this repository perform three distinct tasks: + +1. Format Swift source files using [nicklockwood/SwiftFormat](https://github.com/nicklockwood/SwiftFormat); +2. Test that the sources in the workspace directory match the formatted sources; and +3. Copy the formatted source files to the workspace directory. + +The following sections provide more detail on how these are implemented. + +### Format + +The `swiftformat_pkg` macro defines a build target for each Swift source file in its `srcs` +attribute. This build target creates a formatted version of the source file in the output directory. +Hence, whenever `bazel build` or `bazel test` are invoked, Bazel magic determines if the source file +has changed and executes the corresponding format build target. So, there are two important points +to note: + +1. Only Swift source files that are new or have changed will be formatted. +2. The formatted source files only exist in the output directory after the build step. + +### Test + +The `swiftformat_pkg` macro defines a test target for each Swift source file in its `srcs` +attribute. The test target compares the output of the format build target mentioned above with the +actual source file. If the two files differ, the test fails. As you might expect, these targets are +exercised by running `bazel test`. + +### Update/Copy to the Workspace Directory + +Each Bazel package that has a `swiftformat_pkg` declaration contains a runnable target that, when +executed, copies the formatted source files for the Bazel package from the output directory to the +workspace/source directory. + +For instance, if you defined your `swiftformat_pkg` with the name `swiftformat`, the runnable target +is called `swiftformat_update`. So, to update the files in a single package you would run: + +```sh +$ bazel run //path/to/pkg:swiftformat_update +``` + +Since finding and running these commands manually would be tedious, we added a +`swiftformat_update_all` declaration at the root of the workspace. This will find all of the update +commands and run them. + +```sh +$ bazel run //:update_all +``` + +## Putting It All Together + +So, the typical development workflow goes something like this: + +1. Make a code change. +2. Build and test: `bazel test //...`. +3. If a format test fails, copy the formatted files to your workspace: `bazel run //:update_all`. + +If like most developers, you have integrated SwiftFormat into your IDE. There is a very good +chance that you will never need to perform the update step as your source code will already be +formatted. diff --git a/doc/integrate_with_rules_swift.md b/doc/integrate_with_rules_swift.md new file mode 100644 index 0000000..942ca94 --- /dev/null +++ b/doc/integrate_with_rules_swift.md @@ -0,0 +1,118 @@ +# How to Integrate `rules_swiftformat` Into Your Project + +You have decided to integrate Swift source code formatting into your project using +`rules_swiftformat`. Now, the question is what is the best way to do so. + +## Options + +### Option #1: Add `swiftformat_pkg` to each Bazel package + +This is pretty straightforward. Either manually or using +[Buildozer](https://github.com/bazelbuild/buildtools/tree/master/buildozer), you can add the +[`swiftformat_pkg`](/doc/rules_and_macros_overview.md#swiftformat_pkg) macro to the Bazel packages +that contain Swift source files. This is the technique that is described in [the +quickstart](/README.md#quickstart) and [the simple example](/examples/simple). + +However, this option has one downside. There is no easy way to ensure that `swiftformat_pkg` is +added to any new Bazel packages that are created in the future. + +### Option #2: Combine the build and format declarations + +Wouldn't it be great if we could combine the build and format declarations into one declaration? +Yes, it would. Luckily, Bazel supports user-defined +[macros](https://docs.bazel.build/versions/main/skylark/macros.html). They allow us to combine +multiple declarations into a convenient and tidy declaration. + +After implementing `rules_swiftformat`, we began rolling it out to our other projects. We +immediately realized that we wanted to make it very simple to define a Swift build target and +formatting target. We assumed that other folks would want to do the same. So, we implemented +[`swiftformat_library`](/doc/rules_and_macros_overview.md#swiftformat_library), +[`swiftformat_binary`](/doc/rules_and_macros_overview.md#swiftformat_binary), and +[`swiftformat_test`](/doc/rules_and_macros_overview.md#swiftformat_test). These macros define +the corresponding `swift_XXX` declaration and the appropriate `swiftformat_pkg` declaration. This +technique is demonstrated in our [rules_swift_helpers example](/examples/rules_swift_helpers). + +In short, the `swiftformat_XXX` macros take all of the same attributes as their `swift_XXX` +counterparts. In addition, they have two other attributes: +[`swiftformat_config`](/doc/rules_and_macros_overview.md#swiftformat_library-swiftformat_config) and +a [`swiftformat_name`](/doc/rules_and_macros_overview.md#swiftformat_library-swiftformat_name). +These can be used to customize the formatting for the package if desired. + +The following shows a `BUILD.bazel` file that uses the +[`swiftformat_library`](/doc/rules_and_macros_overview.md#swiftformat_library) macro. + +```python +load( + "@cgrindel_rules_swiftformat//swiftformat:swiftformat.bzl", + "swiftformat_library", +) + +swiftformat_library( + name = "Foo", + srcs = glob(["*.swift"]), + module_name = "Foo", + visibility = ["//:__subpackages__"], +) +``` + +### Option #3: Custom `swift_XXX` macros + +Perhaps the idea of combining the build and formatting declarations sounds great to you. However, +you don't like the idea of introducing an unfamiliar declaration like `swiftformat_XXX`. Or, perhaps +you just want to consolidate your build declarations with some project-specific defaults. Well, you +may want to define your own `swift_library`, `swift_binary`, and `swift_test` macros. We +use this technique in our [Swift Toolbox repository](https://github.com/cgrindel/swift_toolbox). + +Create the `build/swift` directories at the root of your workspace and add a `BUILD.bazel` file. + +```sh +$ mkdir -p build/swift +$ echo "# Intentionally blank" >> build/swift/BUILD.bazel +``` + +Now, create a `swift_library.bzl` file with the following contents: + +```python +load( + "@cgrindel_rules_swiftformat//swiftformat:swiftformat.bzl", + "swiftformat_library", +) + +def swift_library(name, srcs = None, **kwargs): + if srcs == None: + srcs = native.glob(["*.swift"]) + + swiftformat_library( + name = name, + srcs = srcs, + **kwargs + ) +``` + +Next, create a `swift_test.bzl` file and a `swift_binary.bzl` file using the same template as above. +Be sure to change `_library` to the appropriate suffix. + +Lastly, update the load statements for all of the Bazel build files that contain `swift_XXX` +declarations so that they load your macro instead of the original rule. Change +`load("@build_bazel_rules_swift//swift:swift.bzl", "swift_XXX") ` to +`load("//build/swift:swift_library.bzl", "swift_XXX")`. + +Here is an example of an updated `BUILD.bazel` file. + +```python +load("//build/swift:swift_library.bzl", "swift_library") + +swift_library( + name = "DateUtils", + module_name = "DateUtils", + visibility = ["//visibility:public"], +) +``` + + +## Conclusion + +As we have seen, there are several ways to integrate `rules_swiftformat` into your Bazel project. +All of them are valid. However, we would recommend using either Option #2 or Option #3 if you are +implementing this for a large repository or a repository that has multiple contributors. It is less +likely that new Bazel packages will be implemented without the desired formatting capabilities. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..96d6fb8 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,7 @@ +# Examples for `rules_swiftformat` + +- [Simple](simple/) - Demonstrates the use of `swiftformat_pkg`. +- [Swift Rules Helpers](rules_swift_helpers/) - Demonstrates the use of +[`swiftformat_library`](/doc/rules_and_macros_overview.md#swiftformat_library), +[`swiftformat_binary`](/doc/rules_and_macros_overview.md#swiftformat_binary), and +[`swiftformat_test`](/doc/rules_and_macros_overview.md#swiftformat_test). diff --git a/examples/swift_rule_helpers/.swiftformat b/examples/rules_swift_helpers/.swiftformat similarity index 100% rename from examples/swift_rule_helpers/.swiftformat rename to examples/rules_swift_helpers/.swiftformat diff --git a/examples/swift_rule_helpers/BUILD.bazel b/examples/rules_swift_helpers/BUILD.bazel similarity index 100% rename from examples/swift_rule_helpers/BUILD.bazel rename to examples/rules_swift_helpers/BUILD.bazel diff --git a/examples/rules_swift_helpers/README.md b/examples/rules_swift_helpers/README.md new file mode 100644 index 0000000..1bf631e --- /dev/null +++ b/examples/rules_swift_helpers/README.md @@ -0,0 +1,7 @@ +# Example Demonstrating `rules_swift` Convenience Macros + +This example demonstrates the use of +[`swiftformat_library`](/doc/rules_and_macros_overview.md#swiftformat_library), +[`swiftformat_binary`](/doc/rules_and_macros_overview.md#swiftformat_binary), and +[`swiftformat_test`](/doc/rules_and_macros_overview.md#swiftformat_test) +to define `rules_swift` targets along with `rules_swiftformat` targets. diff --git a/examples/swift_rule_helpers/Sources/App/BUILD.bazel b/examples/rules_swift_helpers/Sources/App/BUILD.bazel similarity index 100% rename from examples/swift_rule_helpers/Sources/App/BUILD.bazel rename to examples/rules_swift_helpers/Sources/App/BUILD.bazel diff --git a/examples/swift_rule_helpers/Sources/App/main.swift b/examples/rules_swift_helpers/Sources/App/main.swift similarity index 100% rename from examples/swift_rule_helpers/Sources/App/main.swift rename to examples/rules_swift_helpers/Sources/App/main.swift diff --git a/examples/swift_rule_helpers/Sources/Foo/BUILD.bazel b/examples/rules_swift_helpers/Sources/Foo/BUILD.bazel similarity index 100% rename from examples/swift_rule_helpers/Sources/Foo/BUILD.bazel rename to examples/rules_swift_helpers/Sources/Foo/BUILD.bazel diff --git a/examples/swift_rule_helpers/Sources/Foo/Message.swift b/examples/rules_swift_helpers/Sources/Foo/Message.swift similarity index 100% rename from examples/swift_rule_helpers/Sources/Foo/Message.swift rename to examples/rules_swift_helpers/Sources/Foo/Message.swift diff --git a/examples/swift_rule_helpers/Tests/AppTests/BUILD.bazel b/examples/rules_swift_helpers/Tests/AppTests/BUILD.bazel similarity index 100% rename from examples/swift_rule_helpers/Tests/AppTests/BUILD.bazel rename to examples/rules_swift_helpers/Tests/AppTests/BUILD.bazel diff --git a/examples/swift_rule_helpers/Tests/AppTests/simple_test.sh b/examples/rules_swift_helpers/Tests/AppTests/simple_test.sh similarity index 100% rename from examples/swift_rule_helpers/Tests/AppTests/simple_test.sh rename to examples/rules_swift_helpers/Tests/AppTests/simple_test.sh diff --git a/examples/swift_rule_helpers/Tests/FooTests/BUILD.bazel b/examples/rules_swift_helpers/Tests/FooTests/BUILD.bazel similarity index 100% rename from examples/swift_rule_helpers/Tests/FooTests/BUILD.bazel rename to examples/rules_swift_helpers/Tests/FooTests/BUILD.bazel diff --git a/examples/swift_rule_helpers/Tests/FooTests/MessageTests.swift b/examples/rules_swift_helpers/Tests/FooTests/MessageTests.swift similarity index 100% rename from examples/swift_rule_helpers/Tests/FooTests/MessageTests.swift rename to examples/rules_swift_helpers/Tests/FooTests/MessageTests.swift diff --git a/examples/swift_rule_helpers/WORKSPACE b/examples/rules_swift_helpers/WORKSPACE similarity index 93% rename from examples/swift_rule_helpers/WORKSPACE rename to examples/rules_swift_helpers/WORKSPACE index 87faa21..9c00b3f 100644 --- a/examples/swift_rule_helpers/WORKSPACE +++ b/examples/rules_swift_helpers/WORKSPACE @@ -1,4 +1,4 @@ -workspace(name = "swift_rule_helpers_example") +workspace(name = "rules_swift_helpers_example") local_repository( name = "cgrindel_rules_swiftformat", diff --git a/examples/simple/README.md b/examples/simple/README.md new file mode 100644 index 0000000..7da9fe8 --- /dev/null +++ b/examples/simple/README.md @@ -0,0 +1,3 @@ +# Simple Example + +Demonstrates the use of `swiftformat_pkg`. diff --git a/examples/swift_rule_helpers/README.md b/examples/swift_rule_helpers/README.md deleted file mode 100644 index 5889ed9..0000000 --- a/examples/swift_rule_helpers/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Example Demonstrating `rules_swift` Convenience Macros - -This example demonstrates the use of `swiftformat_library`, `swiftformat_binary`, and -`swiftformat_test` to define `rules_swift` targets along with `rules_swiftformat` targets.