{"id":24660035,"url":"https://github.com/unmango/go-make","last_synced_at":"2026-04-19T05:07:58.206Z","repository":{"id":272223558,"uuid":"915810855","full_name":"unmango/go-make","owner":"unmango","description":"Makefile parsing library for Go","archived":false,"fork":false,"pushed_at":"2025-07-01T07:56:47.000Z","size":157,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-01T08:39:52.212Z","etag":null,"topics":["ast","go","golang","make","makefile","parser","printer","scanner"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unmango.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-12T21:23:37.000Z","updated_at":"2025-07-01T07:56:50.000Z","dependencies_parsed_at":"2025-03-21T06:27:34.269Z","dependency_job_id":"e8194289-ba58-459f-bfe6-c635f40027b9","html_url":"https://github.com/unmango/go-make","commit_stats":null,"previous_names":["unmango/go-make"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/unmango/go-make","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmango%2Fgo-make","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmango%2Fgo-make/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmango%2Fgo-make/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmango%2Fgo-make/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unmango","download_url":"https://codeload.github.com/unmango/go-make/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unmango%2Fgo-make/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270336672,"owners_count":24566778,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-13T02:00:09.904Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","go","golang","make","makefile","parser","printer","scanner"],"created_at":"2025-01-26T03:17:03.622Z","updated_at":"2026-04-19T05:07:58.198Z","avatar_url":"https://github.com/unmango.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go Make\n\n![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/unmango/go-make/ci.yml)\n![GitHub branch check runs](https://img.shields.io/github/check-runs/unmango/go-make/main)\n![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/unmango/go-make)\n![Codecov](https://img.shields.io/codecov/c/github/unmango/go-make)\n![GitHub Release](https://img.shields.io/github/v/release/unmango/go-make)\n![GitHub Release Date](https://img.shields.io/github/release-date/unmango/go-make)\n\nMakefile parsing and utilities in Go\n\n## Usage\n\n### Reading\n\nThe `make.Parser` is the primary way to read Makefiles.\n\n```go\nf := os.Open(\"Makefile\")\np := make.NewParser(f, nil)\n\nm, err := p.ParseFile()\n\nfmt.Println(m.Rules)\n```\n\nThe more primitive `make.Scanner` and `make.ScanTokens` used by `make.Parser` can be used individually.\n\nUsing `make.ScanTokens` with a `bufio.Scanner`\n\n```go\nf := os.Open(\"Makefile\")\ns := bufio.NewScanner(f)\ns.Split(make.ScanTokens)\n\nfor s.Scan() {\n  s.Bytes() // The current token byte slice i.e. []byte(\":=\")\n  s.Text() // The current token as a string i.e. \":=\"\n}\n```\n\nUsing `make.Scanner`\n\n```go\nf := os.Open(\"Makefile\")\ns := make.NewScanner(f, nil)\n\nfor pos, tok, lit := s.Scan(); tok != token.EOF; {\n  fmt.Println(pos) // The position of tok\n  fmt.Println(tok) // The current token.Token i.e. token.SIMPLE_ASSIGN\n  fmt.Println(lit) // Literal tokens as a string i.e. \"identifier\"\n}\n\nif err := s.Err(); err != nil {\n  fmt.Println(err)\n}\n```\n\n### Writing\n\nUse `make.Fprint` to write ast nodes.\n\n\u003e **Note**\n\u003e The AST in this project is a made-up, package-specific representation for Makefiles. It is not an official GNU Make or POSIX AST.\n\n```go\nvar file *ast.File\n\nn, err := make.Fprint(os.Stdout, file)\n```\n\nThe `make.Writer` can be used to incrementally write make syntax to an `io.Writer`.\n\n```go\nbuf := \u0026bytes.Buffer{}\nw := make.NewWriter(buf)\n\nn, err := w.WriteRule(\u0026ast.Rule{})\n```\n\n### Builder\n\nThe `builder` package contains utilities for building AST nodes.\n\n🚧 This API is not stable yet 🚧\n\n```go\nf := builder.NewFile(1,\n  file.WithRule(expr.Text(\"target1\"),\n    rule.WithVarRefTarget(\"FOO\")\n  ),\n)\n\nmake.Fprint(os.Stdout, f)\n// target1 ${FOO}:\\n\n```\n\n## Features\n\n### Syntax Support\n\nMakefile syntax that is guaranteed to round-trip (parse and print without modification) is listed in [./testdata/roundtrip](./testdata/roundtrip/).\nAdditional syntax is supported and may round-trip successfully, but no guarentees are provided until it is listed under `./testdata/roundtrip`.\n\n| Syntax                               | Example                                  |       Parser       |      Printer       |      Builder       | Remarks                                                              |\n| ------------------------------------ | ---------------------------------------- | :----------------: | :----------------: | :----------------: | -------------------------------------------------------------------- |\n| newline escaping                     | `\\trecipe text\\\\ncontinued on next line` |                    |                    |                    |                                                                      |\n| newline separated elements           | `target:\\n\\ntarget2:`                    |                    |                    |                    |                                                                      |\n| **comments**                         |                                          |                    |                    |                    |                                                                      |\n| top-level comments                   | `# comment text`                         | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| comment groups                       | `# comment text\\n# more comment text`    | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| rule comments                        | `target: # comment text`                 |                    |                    |                    |                                                                      |\n| recipe comments                      | `target:\\n\\trecipe # comment text\\n`     | :white_check_mark: | :white_check_mark: |                    | these are not make comments and are included in the recipe text      |\n| **rules**                            |                                          |                    |                    |                    |                                                                      |\n| targets                              | `target:`, `target :`                    | :white_check_mark: | :white_check_mark: | :white_check_mark: |                                                                      |\n| multiple targets                     | `target1 target2:`                       | :white_check_mark: | :white_check_mark: | :white_check_mark: |                                                                      |\n| pre-requisites                       | `target: prereq`                         | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| order-only pre-requisites            | `target: \\| prereq`                      | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| recipes                              | `\\trecipe text\\n`                        | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| recipe with a custom `.RECIPEPREFIX` | `\\|recipe text\\n`                        |                    |                    |                    |                                                                      |\n| semimcolon delimited recipes         | `target: ;recipe text\\n`                 |                    |                    |                    |                                                                      |\n| **variables**                        |                                          |                    |                    |                    |                                                                      |\n| empty declarations                   | `VAR :=`                                 | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| simple declarations                  | `VAR := foo.c bar.c`                     | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| all assigment operators              | `VAR != foo`, `VAR ::= bar`, etc.        | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| **variable references**              |                                          |                    |                    |                    |                                                                      |\n| in targets                           | `${VAR}:`, `$(FOO) $(BAR):`              | :white_check_mark: | :white_check_mark: | :white_check_mark: |                                                                      |\n| in prereqs                           | `target: ${FOO}`                         | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| in recipes                           | `target:\\n\\trecipe $(VAR)\\n`             |                    |                    |                    |                                                                      |\n| **directives**                       |                                          |                    |                    |                    |                                                                      |\n| top-level directives                 | `ifeq`, `define`, etc.                   |                    |                    |                    |                                                                      |\n| conditional directives               | `ifeq`, `ifneq`, `ifdef`, `ifndef`       | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| equality directives                  | `ifeq`, `ifneq`                          | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| parentheses syntax                   | `ifeq (foo, bar)`                        | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| double quotes                        | `ifeq \"foo\" \"bar\"`                       | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| single quotes                        | `ifeq 'foo' 'bar'`                       | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| mixed syntax                         | `ifeq \"foo\" 'bar'`                       | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| definition directives                | `ifdef`, `ifndef`                        | :white_check_mark: | :white_check_mark: |                    |                                                                      |\n| logging directives                   | `$(info message)`                        |                    |                    |                    |                                                                      |\n| expressions                          | `$(shell script stuff)`                  |                    |                    |                    |                                                                      |\n| many other things                    |                                          |                    |                    |                    | please open an issue if there is anything missing you'd like to see! |\n\n### Will Not Support\n\nNothing, at this time\n\n## Workflow\n\n### Pre-Requisites\n\nGo toolchain for the version listed in [go.mod](./go.mod)\n\n### Building\n\ngo-make is itself built using `make`.\n\n|      Targets | Description                                               |\n| -----------: | :-------------------------------------------------------- |\n| default goal | Runs the `build` target                                   |\n|      `build` | Runs `go build` to verify the code compiles               |\n|       `test` | Test changed packages                                     |\n|   `test_all` | Test all packages                                         |\n|      `clean` | Remove `.make` directory and coverage report              |\n|      `cover` | Collect coverage for all tests and print report           |\n|       `tidy` | Runs `go mod tidy`                                        |\n|        `dev` | Setup the [developer environment](#developer-environment) |\n\n### Developer Environment\n\nApart from the Go toolchain, the only main dependency is the `ginkgo` cli to run tests.\nThis repo also uses [devctl](https://github.com/unmango/devctl) but its use is optional.\nTargets will obtain dependencies automatically as needed.\n\nBinaries are stored in a `.gitignore`d `bin/` directory at the root of the repository.\nAn example `.envrc` file for [direnv](https://github.com/direnv/direnv) is provided in [hack/example.envrc](./hack/example.envrc) to add `./bin` to your `PATH` automatically.\nTo use it, run `make .envrc` or `make dev`.\nThis will copy `hack/example.envrc` to `.envrc` at the root of the repository.\n\n## References\n\nGNU Make Quick Reference: \u003chttps://www.gnu.org/software/make/manual/html_node/Quick-Reference.html\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funmango%2Fgo-make","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funmango%2Fgo-make","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funmango%2Fgo-make/lists"}