Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ While [rules_pkg](https://github.com/bazelbuild/rules_pkg/) _works_, `rules_nfpm
- No host dependencies (e.g. `rpmbuild` is not needed in `PATH`)
- Built-in templating of Bazel [workspace status](https://docs.bazel.build/versions/master/user-manual.html#workspace_status) key-value pairs

A [plugin](https://github.com/bazelbuild/bazel-gazelle/blob/master/extend.rst) for [gazelle](https://github.com/bazelbuild/bazel-gazelle) is included that generates the rules and updates their dependencies.

## Setup

Include the following snippet in your repository's WORKSPACE file:
Expand Down
2 changes: 2 additions & 0 deletions docs/readme-header.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ While [rules_pkg](https://github.com/bazelbuild/rules_pkg/) _works_, `rules_nfpm
- No host dependencies (e.g. `rpmbuild` is not needed in `PATH`)
- Built-in templating of Bazel [workspace status](https://docs.bazel.build/versions/master/user-manual.html#workspace_status) key-value pairs

A [plugin](https://github.com/bazelbuild/bazel-gazelle/blob/master/extend.rst) for [gazelle](https://github.com/bazelbuild/bazel-gazelle) is included that generates the rules and updates their dependencies.

## Setup

Include the following snippet in your repository's WORKSPACE file:
Expand Down
13 changes: 13 additions & 0 deletions example/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
load("@bazel_gazelle//:def.bzl", "DEFAULT_LANGUAGES", "gazelle", "gazelle_binary")

gazelle_binary(
name = "gazelle_binary",
languages = DEFAULT_LANGUAGES + [
"@com_github_ericnorris_rules_nfpm//nfpm:gazelle_nfpm_language",
],
)

# gazelle:prefix github.com/ericnorris/rules_nfpm/example
gazelle(
name = "gazelle",
gazelle = ":gazelle_binary",
)
36 changes: 34 additions & 2 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# rules_nfpm example

This folder contains an example bazel WORKSPACE and BUILD setup that builds a small `go` binary and packages the binary into `deb` and `rpm` formats.
This folder contains an example bazel WORKSPACE and BUILD setup that builds a small `go` binary and packages the binary into `deb` and `rpm` formats. The BUILD files for the packages was generated by Gazelle.

## Usage

```
```console
$ bazel build packages/...

$ rpm -qil -v ./bazel-bin/packages/helloworld.rpm
Expand Down Expand Up @@ -37,6 +37,38 @@ helloworld prints "hello, world" and then exits. This particular package was gen
-r-xr-xr-x 1 root root 2169992 Jun 13 15:46 /usr/bin/helloworld
```

### Gazelle

```console
$ rm packages/BUILD.bazel

$ bazel run //:gazelle
INFO: Analyzed target //:gazelle (1 packages loaded, 3 targets configured).
INFO: Found 1 target...
Target //:gazelle up-to-date:
bazel-bin/gazelle-runner.bash
bazel-bin/gazelle
INFO: Elapsed time: 0.775s, Critical Path: 0.06s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action

$ cat packages/BUILD.bazel
load("@com_github_ericnorris_rules_nfpm//nfpm:defs.bzl", "nfpm_package")

nfpm_package(
name = "helloworld.deb",
config = "helloworld.nfpm.yaml",
deps = ["//cmd/helloworld"],
)

nfpm_package(
name = "helloworld.rpm",
config = "helloworld.nfpm.yaml",
deps = ["//cmd/helloworld"],
)
```

## Internals

The directory's [.bazelrc](/example/.bazelrc) file configures the [workspace-status.sh](/example/workspace-status.sh) script to run on every `bazel build` invocation. The [workspace status](https://docs.bazel.build/versions/master/user-manual.html#workspace_status) script produces "stable" and "volatile" key-value pairs that are useable later; the values are hardcoded for the purposes of this example, but could be dynamically computed via `git` commands.
Expand Down
4 changes: 2 additions & 2 deletions example/cmd/helloworld/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
name = "helloworld_lib",
srcs = ["main.go"],
importpath = "github.com/ericnorris/rules_nfpm/example/cmd/helloworld",
visibility = ["//visibility:private"],
)

go_binary(
name = "helloworld",
embed = [":go_default_library"],
embed = [":helloworld_lib"],
visibility = ["//visibility:public"],
)
16 changes: 6 additions & 10 deletions example/packages/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
load("@com_github_ericnorris_rules_nfpm//nfpm:defs.bzl", "nfpm_package")

nfpm_package(
name = "helloworld.rpm",
config = "helloworld.yaml",
deps = [
"//cmd/helloworld",
],
name = "helloworld.deb",
config = "helloworld.nfpm.yaml",
deps = ["//cmd/helloworld"],
)

nfpm_package(
name = "helloworld.deb",
config = "helloworld.yaml",
deps = [
"//cmd/helloworld",
],
name = "helloworld.rpm",
config = "helloworld.nfpm.yaml",
deps = ["//cmd/helloworld"],
)
File renamed without changes.
16 changes: 16 additions & 0 deletions go/internal/gazelle/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "gazelle",
srcs = ["main.go"],
importpath = "github.com/ericnorris/rules_nfpm/go/internal/gazelle",
visibility = ["//:__subpackages__"],
deps = [
"@bazel_gazelle//config:go_default_library",
"@bazel_gazelle//label:go_default_library",
"@bazel_gazelle//language:go_default_library",
"@bazel_gazelle//repo:go_default_library",
"@bazel_gazelle//resolve:go_default_library",
"@bazel_gazelle//rule:go_default_library",
],
)
157 changes: 157 additions & 0 deletions go/internal/gazelle/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Package gazelle is a gazelle plugin that adds nfpm_package rules for each .nfpm.yaml file it finds.
package gazelle

import (
"flag"
"io/ioutil"
"log"
"path/filepath"
"regexp"
"strings"

"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/label"
"github.com/bazelbuild/bazel-gazelle/language"
"github.com/bazelbuild/bazel-gazelle/repo"
"github.com/bazelbuild/bazel-gazelle/resolve"
"github.com/bazelbuild/bazel-gazelle/rule"
)

type nfpmLanguage struct{}

// NewLanguage returns a gazelle language plugin.
func NewLanguage() language.Language {
return &nfpmLanguage{}
}

// Name returns the name of the language. This should be a prefix of the kinds of rules generated by
// the language, e.g., "go" for the Go extension since it generates "go_library" rules.
//
// Since we are generating nfpm_package rules, we return "nfpm".
func (nfpmLanguage) Name() string {
return "nfpm"
}

// Kinds returns a map of maps rule names (kinds) and information on how to match and merge
// attributes that may be found in rules of those kinds. All kinds of rules generated for this
// language may be found here.
func (nfpmLanguage) Kinds() map[string]rule.KindInfo {
return map[string]rule.KindInfo{
"nfpm_package": {
// MatchAttrs is a list of attributes used in matching. For example, for go_library, this list
// contains "importpath". Attributes are matched in order.
//
// Because each config file may be used for both a debian and an rpm package, we cannot match
// on config, so we match on name.
MatchAttrs: []string{"name"},

// ResolveAttrs is a set of attributes that should be merged after
// dependency resolution. See rule.Merge.
ResolveAttrs: map[string]bool{"deps": true},
},
}
}

// Loads returns .bzl files and symbols they define. Every rule generated by
// GenerateRules, now or in the past, should be loadable from one of these
// files.
func (nfpmLanguage) Loads() []rule.LoadInfo {
return []rule.LoadInfo{
{
Name: "@com_github_ericnorris_rules_nfpm//nfpm:defs.bzl",
Symbols: []string{"nfpm_package"},
},
}
}

var _dependencyRegex = regexp.MustCompile("{{ index .Dependencies `(.*)`}}")

// GenerateRules extracts build metadata from source files in a directory.
// GenerateRules is called in each directory where an update is requested
// in depth-first post-order.
//
// args contains the arguments for GenerateRules. This is passed as a
// struct to avoid breaking implementations in the future when new
// fields are added.
//
// A GenerateResult struct is returned. Optional fields may be added to this
// type in the future.
//
// Any non-fatal errors this function encounters should be logged using
// log.Print.
func (nfpmLanguage) GenerateRules(args language.GenerateArgs) language.GenerateResult {
rules := make([]*rule.Rule, 0)
imports := make([]interface{}, 0)
for _, file := range args.RegularFiles {
if strings.HasSuffix(file, ".nfpm.yaml") {
deps := dependenciesFromYaml(args.Dir, file, "//"+args.Rel)
baseName := file[:len(file)-len(".nfpm.yaml")]

deb := makeRule(baseName+".deb", file, deps)
rpm := makeRule(baseName+".rpm", file, deps)

rules = append(rules, deb, rpm)
imports = append(imports, nil, nil)
}
}

return language.GenerateResult{
Gen: rules,
Imports: imports,
}
}

func dependenciesFromYaml(dir, file, rel string) []string {
// ioutil.ReadFile can be replaced with os.ReadFile when we rely on go 1.16.
bytes, err := ioutil.ReadFile(filepath.Join(dir, file))
if err != nil {
log.Printf("Failed to read file due to %+v", err)
return nil
}
matches := _dependencyRegex.FindAllStringSubmatch(string(bytes), -1)
deps := make([]string, len(matches))
for i, ms := range matches {
m := ms[1]
if strings.HasPrefix(m, rel) {
deps[i] = m[len(rel):]
} else {
deps[i] = m
}
}
return deps
}

func makeRule(name, config string, deps []string) *rule.Rule {
r := rule.NewRule("nfpm_package", name)
r.SetAttr("config", config)
r.SetAttr("deps", deps)
return r
}

func (nfpmLanguage) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
}

func (nfpmLanguage) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
return nil
}

func (nfpmLanguage) KnownDirectives() []string {
return nil
}

func (nfpmLanguage) Configure(c *config.Config, rel string, f *rule.File) {
}

func (nfpmLanguage) Fix(c *config.Config, f *rule.File) {
}

func (nfpmLanguage) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
return nil
}

func (nfpmLanguage) Embeds(r *rule.Rule, from label.Label) []label.Label {
return nil
}

func (nfpmLanguage) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, imports interface{}, from label.Label) {
}
6 changes: 6 additions & 0 deletions nfpm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ filegroup(
srcs = glob(["*"]),
visibility = ["//:__subpackages__"],
)

alias(
name = "gazelle_nfpm_language",
actual = "//go/internal/gazelle",
visibility = ["//visibility:public"],
)
14 changes: 12 additions & 2 deletions scripts/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
load("@bazel_gazelle//:def.bzl", "gazelle")
load("@bazel_gazelle//:def.bzl", "DEFAULT_LANGUAGES", "gazelle", "gazelle_binary")
load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier")

#
# Gazelle scripts
#

gazelle(name = "gazelle")
gazelle_binary(
name = "gazelle_binary",
languages = DEFAULT_LANGUAGES + [
"//nfpm:gazelle_nfpm_language",
],
)

gazelle(
name = "gazelle",
gazelle = ":gazelle_binary",
)

sh_binary(
name = "generate-go-build-files",
Expand Down
21 changes: 7 additions & 14 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
load("//nfpm:defs.bzl", "nfpm_package")

filegroup(
name = "text-files",
srcs = glob([
"*.txt",
]),
)
load("@com_github_ericnorris_rules_nfpm//nfpm:defs.bzl", "nfpm_package")

nfpm_package(
name = "test.rpm",
config = "test-config.yaml",
deps = [":text-files"],
name = "test.deb",
config = "test.nfpm.yaml",
deps = [":meow.txt"],
)

nfpm_package(
name = "test.deb",
config = "test-config.yaml",
deps = [":text-files"],
name = "test.rpm",
config = "test.nfpm.yaml",
deps = [":meow.txt"],
)
File renamed without changes.