Skip to content

Commit

Permalink
Implement directives to set the repos for dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
facundominguez committed Jul 13, 2021
1 parent d354ee8 commit 1c22dc7
Show file tree
Hide file tree
Showing 27 changed files with 452 additions and 47 deletions.
1 change: 1 addition & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
example
tests/alternative-deps
19 changes: 15 additions & 4 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
steps:
- label: "Build"
- label: "Build and unit tests"
command: |
echo "build --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host" > .bazelrc.local
echo build --repository_cache=~/.bazel_repo_cache >> .bazelrc.local
echo build --disk_cache=~/.bazel_disk_cache >> .bazelrc.local
nix-shell --pure --run 'bazel build //...'

This comment has been minimized.

Copy link
@aherrmann

aherrmann Jul 14, 2021

Member

bazel build //... is redundant next to bazel test //.... See bazelbuild/bazel#4257 (comment). Running bazel test //... only can be a bit faster as Bazel can interleave build steps and test execution.

nix-shell --pure --run 'bazel test //...'
timeout: 30
- label: "Test"
- label: "Test the example"
command: |
nix-shell --pure --run 'bazel test //...'
ln -sr .bazelrc.local example/.bazelrc.local
cd example
nix-shell --pure --run 'bazel run //:gazelle'
nix-shell --pure --run 'bazel run //:gazelle-update-repos'
nix-shell --pure --run 'bazel build //...'
nix-shell --pure --run 'bazel test //...'
timeout: 30
- label: "Test alternative dependencies"
command: |
nix-shell --pure --run 'bazel test //...'
ln -sr .bazelrc.local tests/alternative-deps/.bazelrc.local
cd tests/alternative-deps
nix-shell --pure --run 'bazel run //:gazelle'
nix-shell --pure --run 'bazel run //:gazelle-update-repos'
nix-shell --pure --run 'bazel build //...'
nix-shell --pure --run 'bazel test //...'
timeout: 30
- label: "Test"
- label: "buildifier linting"
command: |
nix-shell --pure --run 'bazel run //:buildifier-diff'
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ labels. The labels are added to the `deps` attribute of the
corresponding rule. Names not mentioned in this directive are
added to the `compiler_flags` attribute as `-l<name>`.

```python
# gazelle:cabal_haskell_package_repo stackage
```
Specifies the name of the repository from where Cabal packages
are provided. The default value is `stackage`. The user is
responsible for setting up the repository. The repository for
tools is constructed automatically by appending `-exe` to this.

## Dependency resolution

In general, package names in the `build-depends` field are mapped to
Expand Down
2 changes: 1 addition & 1 deletion cabalscan/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ haskell_binary(
":cabalscan-library",
":containers",
":text",
"@stackage//:aeson",
"@io_tweag_gazelle_cabal_deps//:aeson",
],
)

Expand Down
69 changes: 39 additions & 30 deletions gazelle_cabal/dependency_resolution.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ type ToolName struct {
ExecutableName string
}

func setToolsAttribute(ix *resolve.RuleIndex, r *rule.Rule, importData ImportData) {
func setToolsAttribute(
ix *resolve.RuleIndex,
toolRepo string,
r *rule.Rule,
importData ImportData,
) {
tools := make([]string, 0, len(importData.Tools))
for _, tool := range importData.Tools {
toolLabel := findToolLabel(ix, tool)
toolLabel := findToolLabel(ix, toolRepo, tool)
tools = append(tools, toolLabel)
}

Expand All @@ -51,6 +56,7 @@ func setToolsAttribute(ix *resolve.RuleIndex, r *rule.Rule, importData ImportDat
// flags.
func setCompilerFlagsAttribute(
extraLibrariesMap map[string]string,
toolRepo string,
ix *resolve.RuleIndex,
r *rule.Rule,
importData ImportData,
Expand All @@ -63,7 +69,7 @@ func setCompilerFlagsAttribute(

dropToolMacroDefs(importData.CompilerFlags, importData.Tools, &compilerFlags)
addLibraryFlags(extraLibrariesMap, importData.ExtraLibraries, &compilerFlags)
addMacroDefs(ix, importData.Tools, &compilerFlags)
addMacroDefs(ix, toolRepo, importData.Tools, &compilerFlags)
r.SetAttr("compiler_flags", compilerFlags)
}

Expand All @@ -82,9 +88,9 @@ func dropToolMacroDefs(xs []string, tools []ToolName, ys *[]string) {
}

// Adds macros defining the paths for the given tools
func addMacroDefs(ix *resolve.RuleIndex, tools []ToolName, ys *[]string) {
func addMacroDefs(ix *resolve.RuleIndex, toolRepo string, tools []ToolName, ys *[]string) {
for _, tool := range tools {
toolLabel := findToolLabel(ix, tool)
toolLabel := findToolLabel(ix, toolRepo, tool)
macroDef := "-D" + toolMacroName(tool) + "=$(location " + toolLabel + ")"
*ys = append(*ys, macroDef)
}
Expand Down Expand Up @@ -119,7 +125,7 @@ func toolMacroName(tool ToolName) string {
return strings.ToUpper(strings.ReplaceAll(toolString, "-", "_"))
}

func findToolLabel(ix *resolve.RuleIndex, tool ToolName) string {
func findToolLabel(ix *resolve.RuleIndex, toolRepo string, tool ToolName) string {
toolString := tool.ExecutableName
if tool.PackageName == tool.ExecutableName {
toolString = toolString + "-binary"
Expand All @@ -130,16 +136,17 @@ func findToolLabel(ix *resolve.RuleIndex, tool ToolName) string {
if len(res) > 0 {
return res[0].Label.String()
} else {
return "@stackage-exe//" + tool.PackageName + ":" + tool.ExecutableName
return "@" + toolRepo + "//" + tool.PackageName + ":" + tool.ExecutableName
}
}

// All dependencies that are ghc_plugins are placed in the plugins
// attribute. All the dependencies that are indexed comming from
// haskell_library are assumed to be local. There rest of the
// dependencies are assumed to come from @stackage.
// dependencies are assumed to come from packageRepo.
func setDepsAndPluginsAttributes(
extraLibraries map[string]string,
packageRepo string,
ix *resolve.RuleIndex,
r *rule.Rule,
importData ImportData,
Expand All @@ -151,7 +158,7 @@ func setDepsAndPluginsAttributes(
if err == nil {
plugins = append(plugins, plugin)
} else {
deps = append(deps, getPackageLabel(ix, depName))
deps = append(deps, getPackageLabel(ix, packageRepo, depName))
}
}
for _, lib := range importData.ExtraLibraries {
Expand All @@ -177,14 +184,14 @@ func getPluginLabel(ix *resolve.RuleIndex, packageName string) (string, error) {
}

// Produces a label for the given package. We assume that if no rule
// is indexed with the package name, the package must come from stackage.
func getPackageLabel(ix *resolve.RuleIndex, packageName string) string {
// is indexed with the package name, the package must come from packageRepo.
func getPackageLabel(ix *resolve.RuleIndex, packageRepo string, packageName string) string {
spec := resolve.ImportSpec{gazelleCabalName, packageName}
res := ix.FindRulesByImport(spec, gazelleCabalName)
if len(res) > 0 {
return res[0].Label.String()
} else {
return "@stackage//:" + packageName
return "@" + packageRepo + "//:" + packageName
}
}

Expand Down Expand Up @@ -226,7 +233,7 @@ func collectDependenciesFromRepo(
regularFiles,
genFiles []string,
) {
collectDependenciesFromFile(f, packages, tools)
collectDependenciesFromFile(f, c, packages, tools)
},
)
packageList := mapSortedStringKeys(packages)
Expand All @@ -236,28 +243,29 @@ func collectDependenciesFromRepo(

func collectDependenciesFromFile(
f *rule.File,
c *config.Config,
packages map[string]bool,
tools map[string]map[string]bool,
) {
if f == nil {
return
}
packageRepo := c.Exts[gazelleCabalName].(Config).HaskellPackageRepo
toolRepo := packageRepo + "-exe"

for _, r := range f.Rules {
if isHaskellRule(r.Kind()) {
packageLabels := r.AttrStrings("deps")
for _, labelString := range packageLabels {
label, err := label.Parse(labelString)
if err == nil && label.Repo == "stackage" && label.Pkg == "" {
label, err := parseAbsoluteLabel(labelString)
if err == nil && label.Repo == packageRepo && label.Pkg == "" {
packages[label.Name] = true
}
}
toolLabels := r.AttrStrings("tools")
for _, labelString := range toolLabels {
// label.Parse chokes on "@stackage-exe//" with
// label parse error: repository has invalid characters
// https://github.com/bazelbuild/bazel-gazelle/issues/1082
label, err := parseStackageExeLabel(labelString)
if err == nil {
label, err := parseAbsoluteLabel(labelString)
if err == nil && label.Repo == toolRepo {
m, ok := tools[label.Pkg]
if !ok {
m = make(map[string]bool)
Expand Down Expand Up @@ -297,16 +305,17 @@ func mapSortedStringKeys(m map[string]bool) []string {
return ss
}

func parseStackageExeLabel(v string) (label.Label, error) {
if strings.HasPrefix(v, "@stackage-exe//") {
repo := strings.Split(v, "//")
if len(repo) > 1 {
pkg := strings.Split(repo[1], ":")
if len(pkg) > 1 {
return label.New("stackage-exe", pkg[0], pkg[1]), nil
} else {
return label.New("stackage-exe", pkg[0], pkg[0]), nil
}
// label.Parse chokes on hyphenated repo names with
// label parse error: repository has invalid characters
// https://github.com/bazelbuild/bazel-gazelle/issues/1082
func parseAbsoluteLabel(v string) (label.Label, error) {
repo := strings.Split(v, "//")
if len(repo) > 1 && strings.HasPrefix(repo[0], "@") {
pkg := strings.Split(repo[1], ":")
if len(pkg) > 1 {
return label.New(repo[0][1:], pkg[0], pkg[1]), nil
} else {
return label.New(repo[0][1:], pkg[0], pkg[0]), nil
}
}
return label.Label{}, fmt.Errorf("Can't parse: %s", v)
Expand Down
18 changes: 11 additions & 7 deletions gazelle_cabal/dependency_resolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func TestMapSortedStringKeys(t *testing.T) {
func TestParseStackageExeLabel(t *testing.T) {

s := "@stackage-exe//tasty-discover"
got, err := parseStackageExeLabel(s)
got, err := parseAbsoluteLabel(s)
wanted := s
if err != nil {
t.Errorf("got %v, wanted %v", err, nil)
Expand All @@ -173,7 +173,7 @@ func TestParseStackageExeLabel(t *testing.T) {
}

s = "@stackage-exe//tasty-discover:tasty"
got, err = parseStackageExeLabel(s)
got, err = parseAbsoluteLabel(s)
wanted = s
if err != nil {
t.Errorf("got %v, wanted %v", err, nil)
Expand All @@ -183,7 +183,7 @@ func TestParseStackageExeLabel(t *testing.T) {
}

s = "@stackage-exe//tasty-discover:tasty-discover"
got, err = parseStackageExeLabel(s)
got, err = parseAbsoluteLabel(s)
wanted = "@stackage-exe//tasty-discover"
if err != nil {
t.Errorf("got %v, wanted %v", err, nil)
Expand All @@ -192,10 +192,14 @@ func TestParseStackageExeLabel(t *testing.T) {
t.Errorf("got %v, wanted %v", got, wanted)
}

s = "@stackage//tasty-discover:tasty-discover"
_, err = parseStackageExeLabel(s)
if err == nil {
t.Errorf("got nil, wanted some error")
s = "@stackage-exe//tasty-discover:tasty"
got, err = parseAbsoluteLabel(s)
wanted = "(stackage-exe, tasty-discover, tasty)"
if err != nil {
t.Errorf("got %v, wanted %v", err, nil)
}
if got.Repo != "stackage-exe" || got.Pkg != "tasty-discover" || got.Name != "tasty" {
t.Errorf("got (%s, %s, %s) wanted %s", got.Repo, got.Pkg, got.Name, wanted)
}
}

Expand Down
20 changes: 15 additions & 5 deletions gazelle_cabal/lang.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ func (*gazelleCabalLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.C
func (*gazelleCabalLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error { return nil }

func (*gazelleCabalLang) KnownDirectives() []string {
return []string{"cabal_extra_libraries"}
return []string{
"cabal_extra_libraries",
"cabal_haskell_package_repo",
}
}

type Config struct {
ExtraLibrariesMap map[string]string
HaskellPackageRepo string
}

func (*gazelleCabalLang) Configure(c *config.Config, rel string, f *rule.File) {
Expand All @@ -60,13 +64,16 @@ func (*gazelleCabalLang) Configure(c *config.Config, rel string, f *rule.File) {
} else {
extraConfig = Config{
ExtraLibrariesMap: make(map[string]string),
HaskellPackageRepo: "stackage",
}
}

for _, directive := range f.Directives {
switch directive.Key {
case "cabal_extra_libraries":
parseExtraLibraries(&extraConfig, directive.Value)
case "cabal_haskell_package_repo":
extraConfig.HaskellPackageRepo = directive.Value
}
}
c.Exts[gazelleCabalName] = extraConfig
Expand Down Expand Up @@ -144,10 +151,12 @@ func (*gazelleCabalLang) Embeds(r *rule.Rule, from label.Label) []label.Label {

func (*gazelleCabalLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, imports interface{}, from label.Label) {
extraLibrariesMap := c.Exts[gazelleCabalName].(Config).ExtraLibrariesMap
packageRepo := c.Exts[gazelleCabalName].(Config).HaskellPackageRepo
toolRepo := packageRepo + "-exe"
importData := imports.(ImportData)
setDepsAndPluginsAttributes(extraLibrariesMap, ix, r, importData)
setCompilerFlagsAttribute(extraLibrariesMap, ix, r, importData)
setToolsAttribute(ix, r, importData)
setDepsAndPluginsAttributes(extraLibrariesMap, packageRepo, ix, r, importData)
setCompilerFlagsAttribute(extraLibrariesMap, toolRepo, ix, r, importData)
setToolsAttribute(ix, toolRepo, r, importData)
}

func (*gazelleCabalLang) GenerateRules(args language.GenerateArgs) language.GenerateResult {
Expand All @@ -172,7 +181,8 @@ func (*gazelleCabalLang) GenerateRules(args language.GenerateArgs) language.Gene
func (lang *gazelleCabalLang) UpdateRepos(args language.UpdateReposArgs) language.UpdateReposResult {
packageList, components := collectDependenciesFromRepo(args.Config, lang)

r := rule.NewRule("stack_snapshot", "stackage")
packageRepo := args.Config.Exts[gazelleCabalName].(Config).HaskellPackageRepo
r := rule.NewRule("stack_snapshot", packageRepo)
r.SetAttr("packages", packageList)
if len(components) > 0 {
r.SetAttr("components", components)
Expand Down
1 change: 1 addition & 0 deletions tests/alternative-deps/.bazelrc
39 changes: 39 additions & 0 deletions tests/alternative-deps/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
load(
"@bazel_gazelle//:def.bzl",
"DEFAULT_LANGUAGES",
"gazelle",
"gazelle_binary",
)
load("@rules_haskell//haskell:defs.bzl", "ghc_plugin")

# gazelle:cabal_extra_libraries [email protected]//:zlib
# gazelle:cabal_haskell_package_repo stackage-b
gazelle(
name = "gazelle",
data = ["@io_tweag_gazelle_cabal//cabalscan"],
gazelle = "//:gazelle_binary",
)

gazelle_binary(
name = "gazelle_binary",
languages = DEFAULT_LANGUAGES + ["@io_tweag_gazelle_cabal//gazelle_cabal"],
)

gazelle(
name = "gazelle-update-repos",
command = "update-repos",
data = ["@io_tweag_gazelle_cabal//cabalscan"],
extra_args = [
"-lang",
"gazelle_cabal",
"stackage-b",
],
gazelle = "//:gazelle_binary",
)

ghc_plugin(
name = "inspection-testing-plugin",
module = "Test.Inspection.Plugin",
visibility = ["//:__subpackages__"],
deps = ["@stackage-b//:inspection-testing"],
)
Loading

0 comments on commit 1c22dc7

Please sign in to comment.