Skip to content

Commit

Permalink
Added documentation and updated README with 0.1.0 release info (#16)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cgrindel authored Oct 15, 2021
1 parent 80a2745 commit 1da1fb9
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 67 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 //...
92 changes: 33 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<a id="#quickstart"></a>
## 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`

Expand All @@ -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")
Expand Down Expand Up @@ -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.


Expand All @@ -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`.
13 changes: 11 additions & 2 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -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)
65 changes: 65 additions & 0 deletions doc/how_it_works.md
Original file line number Diff line number Diff line change
@@ -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.
118 changes: 118 additions & 0 deletions doc/integrate_with_rules_swift.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -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).
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions examples/rules_swift_helpers/README.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
workspace(name = "swift_rule_helpers_example")
workspace(name = "rules_swift_helpers_example")

local_repository(
name = "cgrindel_rules_swiftformat",
Expand Down
3 changes: 3 additions & 0 deletions examples/simple/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Simple Example

Demonstrates the use of `swiftformat_pkg`.
4 changes: 0 additions & 4 deletions examples/swift_rule_helpers/README.md

This file was deleted.

0 comments on commit 1da1fb9

Please sign in to comment.