Skip to content

Commit 611bc1f

Browse files
authored
fix: local_swift_package relative path resolution (#366)
This fixes the relative path resolution for local_swift_package when the Package.swift is not in the root of the repository. Closes #363
1 parent ea16652 commit 611bc1f

File tree

5 files changed

+42
-9
lines changed

5 files changed

+42
-9
lines changed

docs/repository_rules_overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Used to build a local Swift package.
3131
| <a id="local_swift_package-bazel_package_name"></a>bazel_package_name | The short name for the Swift package's Bazel repository. | String | optional | <code>""</code> |
3232
| <a id="local_swift_package-dependencies_index"></a>dependencies_index | A JSON file that contains a mapping of Swift products and Swift modules. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
3333
| <a id="local_swift_package-env"></a>env | Environment variables that will be passed to the execution environments for this repository rule. (e.g. SPM version check, SPM dependency resolution, SPM package description generation) | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | <code>{}</code> |
34-
| <a id="local_swift_package-path"></a>path | The path to the local Swift package directory. | String | required | |
34+
| <a id="local_swift_package-path"></a>path | The path to the local Swift package directory. This can be an absolute path or a relative path to the workspace root. | String | required | |
3535
| <a id="local_swift_package-repo_mapping"></a>repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.&lt;p&gt;For example, an entry <code>"@foo": "@bar"</code> declares that, for any time this repository depends on <code>@foo</code> (such as a dependency on <code>@foo//some:target</code>, it should actually resolve that dependency within globally-declared <code>@bar</code> (<code>@bar//some:target</code>). | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required | |
3636

3737

gazelle/internal/swift/repo_rule_from_bazel_repo.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type commitProvider interface {
1515

1616
// RepoRuleFromBazelRepo returns a repository rule declaration for a Swift Bazel repository.
1717
// The pkgDir is the path to the Swift package that is referencing this Bazel repository.
18-
func RepoRuleFromBazelRepo(bzlRepo *BazelRepo, diRel string, pkgDir string) (*rule.Rule, error) {
18+
func RepoRuleFromBazelRepo(bzlRepo *BazelRepo, diRel string, pkgDir string, repoDir string) (*rule.Rule, error) {
1919
var r *rule.Rule
2020
var err error
2121
if bzlRepo.Pin != nil {
@@ -24,10 +24,19 @@ func RepoRuleFromBazelRepo(bzlRepo *BazelRepo, diRel string, pkgDir string) (*ru
2424
return nil, err
2525
}
2626
} else {
27-
relPath, err := filepath.Rel(pkgDir, bzlRepo.PkgInfo.Path)
27+
// For local packages, the `path` attribute must be relative to the WORKSPACE file.
28+
// However, since the Package.swift can exist anywhere in the repository, we need to
29+
// resolve the relative path from the package directory and then get the relative path from the WORKSPACE.
30+
relPathFromPkgDir, err := filepath.Rel(pkgDir, bzlRepo.PkgInfo.Path)
2831
if err != nil {
2932
return nil, err
3033
}
34+
absPathFromPkgDir := filepath.Join(pkgDir, relPathFromPkgDir)
35+
relPath, err := filepath.Rel(repoDir, absPathFromPkgDir)
36+
if err != nil {
37+
return nil, err
38+
}
39+
3140
r = repoRuleForLocalPackage(bzlRepo.Name, relPath)
3241
}
3342

gazelle/internal/swift/repo_rule_from_bazel_repo_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import (
1313
)
1414

1515
func TestRepoRuleFromBazelRepo(t *testing.T) {
16-
pkgDir := "/path/to/package"
16+
repoDir := "/path/to/repo"
17+
pkgDir := "/path/to/repo"
1718
diBasename := "swift_deps_index.json"
1819

1920
t.Run("with pin (source control dep)", func(t *testing.T) {
@@ -35,7 +36,7 @@ func TestRepoRuleFromBazelRepo(t *testing.T) {
3536
Name: repoName,
3637
Pin: p,
3738
}
38-
actual, err := swift.RepoRuleFromBazelRepo(br, diBasename, pkgDir)
39+
actual, err := swift.RepoRuleFromBazelRepo(br, diBasename, pkgDir, repoDir)
3940
assert.NoError(t, err)
4041

4142
expected := rule.NewRule(swift.SwiftPkgRuleKind, repoName)
@@ -46,7 +47,8 @@ func TestRepoRuleFromBazelRepo(t *testing.T) {
4647
assert.Equal(t, expected, actual)
4748
})
4849
t.Run("without pin (local Swift package)", func(t *testing.T) {
49-
pkgDir := "/path/to/package"
50+
repoDir := "/path/to/repo"
51+
pkgDir := "/path/to/repo"
5052
relLocalPkgDir := "third_party/cool_local_package"
5153
localPkgDir := filepath.Join(pkgDir, relLocalPkgDir)
5254
repoName := "swiftpkg_cool_local_package"
@@ -58,12 +60,34 @@ func TestRepoRuleFromBazelRepo(t *testing.T) {
5860
Path: localPkgDir,
5961
},
6062
}
61-
actual, err := swift.RepoRuleFromBazelRepo(br, diBasename, pkgDir)
63+
actual, err := swift.RepoRuleFromBazelRepo(br, diBasename, pkgDir, repoDir)
6264
assert.NoError(t, err)
6365

6466
expected := rule.NewRule(swift.LocalSwiftPkgRuleKind, repoName)
6567
expected.SetAttr("path", relLocalPkgDir)
6668
expected.SetAttr("dependencies_index", fmt.Sprintf("@//:%s", diBasename))
6769
assert.Equal(t, expected, actual)
6870
})
71+
t.Run("without pin (local Swift package with relative path)", func(t *testing.T) {
72+
repoDir := "/path/to/repo"
73+
pkgDir := "/path/to/repo/third_party"
74+
relLocalPkgDir := "../cool_local_package"
75+
localPkgDir := filepath.Clean(filepath.Join(pkgDir, relLocalPkgDir))
76+
repoName := "swiftpkg_cool_local_package"
77+
78+
br := &swift.BazelRepo{
79+
Name: repoName,
80+
PkgInfo: &swiftpkg.PackageInfo{
81+
Name: "cool-local-package",
82+
Path: localPkgDir,
83+
},
84+
}
85+
actual, err := swift.RepoRuleFromBazelRepo(br, diBasename, pkgDir, repoDir)
86+
assert.NoError(t, err)
87+
88+
expected := rule.NewRule(swift.LocalSwiftPkgRuleKind, repoName)
89+
expected.SetAttr("path", "cool_local_package")
90+
expected.SetAttr("dependencies_index", fmt.Sprintf("@//:%s", diBasename))
91+
assert.Equal(t, expected, actual)
92+
})
6993
}

gazelle/update_repos.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func importReposFromPackageManifest(args language.ImportReposArgs) language.Impo
150150
for _, bzlRepo := range bzlReposByIdentity {
151151
repoUsage[bzlRepo.Name] = true
152152
var err error
153-
result.Gen[idx], err = swift.RepoRuleFromBazelRepo(bzlRepo, sc.DependencyIndexRel, pkgDir)
153+
result.Gen[idx], err = swift.RepoRuleFromBazelRepo(bzlRepo, sc.DependencyIndexRel, pkgDir, c.RepoRoot)
154154
if err != nil {
155155
result.Error = err
156156
return result

swiftpkg/internal/local_swift_package.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def _local_swift_package_impl(repository_ctx):
6464

6565
_PATH_ATTRS = {
6666
"path": attr.string(
67-
doc = "The path to the local Swift package directory.",
67+
doc = "The path to the local Swift package directory. This can be an absolute path or a relative path to the workspace root.",
6868
mandatory = True,
6969
),
7070
}

0 commit comments

Comments
 (0)