├── .github └── workflows │ ├── codeql.yml │ ├── go.yml │ ├── release.yml │ ├── renovate-config-validator-ci.yml │ └── security.yml ├── .gitignore ├── .goreleaser.yml ├── LICENSE ├── Makefile ├── README.md ├── builder ├── builder.go ├── builder_test.go ├── const.go ├── nginx.go └── staticlibrary.go ├── command └── command.go ├── config ├── configure.example ├── modules.json.brotli ├── modules.json.example └── modules.json.njs ├── configure ├── configure.go ├── configure_test.go ├── configureopt.go ├── generate.go └── normalize.go ├── download.go ├── go.mod ├── go.sum ├── images └── nginx-build.gif ├── message.go ├── module3rd ├── download.go ├── load.go ├── load_test.go ├── module3rd.go └── provide.go ├── multistringflag.go ├── nginx-build.go ├── openresty ├── openresty.go └── openresty_test.go ├── option.go ├── renovate.json ├── util ├── file.go ├── message.go └── patch.go └── version.go /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: ["master"] 17 | pull_request: 18 | branches: ["master"] 19 | schedule: 20 | - cron: "37 22 * * 0" 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners 29 | # Consider using larger runners for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # only required for workflows in private repositories 37 | actions: read 38 | contents: read 39 | 40 | strategy: 41 | fail-fast: false 42 | matrix: 43 | language: ["go"] 44 | # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] 45 | # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both 46 | # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 47 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 48 | 49 | steps: 50 | - name: Checkout repository 51 | uses: actions/checkout@v4 52 | 53 | # Initializes the CodeQL tools for scanning. 54 | - name: Initialize CodeQL 55 | uses: github/codeql-action/init@v3 56 | with: 57 | languages: ${{ matrix.language }} 58 | # If you wish to specify custom queries, you can do so here or in a config file. 59 | # By default, queries listed here will override any specified in a config file. 60 | # Prefix the list here with "+" to use these queries and those in the config file. 61 | 62 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 63 | # queries: security-extended,security-and-quality 64 | 65 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 66 | # If this step fails, then you should remove it and run the build manually (see below) 67 | - name: Autobuild 68 | uses: github/codeql-action/autobuild@v3 69 | 70 | # ℹ️ Command-line programs to run using the OS shell. 71 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 72 | 73 | # If the Autobuild fails above, remove it and uncomment the following three lines. 74 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 75 | 76 | # - run: | 77 | # echo "Run, Build Application using script" 78 | # ./location_of_script_within_repo/buildscript.sh 79 | 80 | - name: Perform CodeQL Analysis 81 | uses: github/codeql-action/analyze@v3 82 | with: 83 | category: "/language:${{matrix.language}}" 84 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set up Go 11 | uses: actions/setup-go@v5 12 | with: 13 | go-version: 1.24.4 14 | - name: Build 15 | run: make 16 | - name: Test 17 | run: make check 18 | 19 | run-nginx-openssl: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Set up Go 24 | uses: actions/setup-go@v5 25 | with: 26 | go-version: 1.24.4 27 | - name: Run Nginx with OpenSSL 28 | run: | 29 | make 30 | ./nginx-build -c ./config/configure.example -m ./config/modules.json.example -d work -clear -pcre -zlib -openssl 31 | 32 | run-nginx-libressl: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: Set up Go 37 | uses: actions/setup-go@v5 38 | with: 39 | go-version: 1.24.4 40 | - name: Run Nginx with LibreSSL 41 | run: | 42 | make 43 | ./nginx-build -c ./config/configure.example -m ./config/modules.json.example -d work -clear -pcre -zlib -libressl 44 | 45 | run-freenginx: 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v4 49 | - name: Set up Go 50 | uses: actions/setup-go@v5 51 | with: 52 | go-version: 1.24.4 53 | - name: Run Nginx with FreeNginx 54 | run: | 55 | make 56 | ./nginx-build -c ./config/configure.example -m ./config/modules.json.example -d work -clear -freenginx -pcre -zlib -openssl 57 | 58 | run-openresty: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v4 62 | - name: Set up Go 63 | uses: actions/setup-go@v5 64 | with: 65 | go-version: 1.24.4 66 | - name: Run Nginx with OpenResty 67 | run: | 68 | make 69 | ./nginx-build -c ./config/configure.example -m ./config/modules.json.example -d work -clear -openresty -pcre -zlib -openssl 70 | 71 | run-nginx-brotli: 72 | runs-on: ubuntu-latest 73 | steps: 74 | - uses: actions/checkout@v4 75 | - name: Set up Go 76 | uses: actions/setup-go@v5 77 | with: 78 | go-version: 1.24.4 79 | - name: Run Nginx with Brotli 80 | run: | 81 | make 82 | ./nginx-build -c ./config/configure.example -m ./config/modules.json.brotli -d work -clear -pcre -zlib -openssl 83 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - "v[0-9]+.[0-9]+.[0-9]+" 6 | jobs: 7 | goreleaser: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | - name: Setup Go 13 | uses: actions/setup-go@v5 14 | with: 15 | go-version: 1.24.4 16 | - name: Run GoReleaser 17 | uses: goreleaser/goreleaser-action@v6 18 | with: 19 | version: latest 20 | args: release --clean 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/renovate-config-validator-ci.yml: -------------------------------------------------------------------------------- 1 | name: Renovate Config Validator 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | paths: 7 | - "renovate.json" 8 | - ".github/workflows/renovate-config-validator-ci.yml" 9 | 10 | jobs: 11 | renovate-config-validator: 12 | timeout-minutes: 10 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: validate renovate.json 20 | run: npx --package=renovate@latest -c renovate-config-validator 21 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | name: govulncheck 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | schedule: 9 | - cron: "0 0 * * *" 10 | 11 | jobs: 12 | govulncheck_job: 13 | runs-on: ubuntu-latest 14 | name: Run govulncheck 15 | steps: 16 | - id: govulncheck 17 | uses: golang/govulncheck-action@v1 18 | with: 19 | go-version-input: 1.24.4 20 | go-package: ./... 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _vendor 2 | nginx-build 3 | .vendor 4 | vendor 5 | doc/_build/doctrees/ 6 | *.tar 7 | *.tar.gz 8 | work/ 9 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: nginx-build 2 | env: 3 | - GO111MODULE=on 4 | builds: 5 | - binary: nginx-build 6 | ldflags: 7 | - -s -w 8 | - -X main.NginxBuildVersion=v{{.Version}} 9 | env: 10 | - CGO_ENABLED=0 11 | goos: 12 | - linux 13 | - darwin 14 | goarch: 15 | - amd64 16 | - arm64 17 | archives: 18 | - name_template: '{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}-{{ .Version }}' 19 | release: 20 | prerelease: auto 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014- Tatsuhiko Kubo 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export GO111MODULE=on 2 | 3 | nginx-build: *.go builder/*.go command/*.go configure/*.go module3rd/*.go openresty/*.go util/*.go 4 | go build -ldflags "-X main.NginxBuildVersion=`git rev-list HEAD -n1`" -o $@ 5 | 6 | build-example: nginx-build 7 | ./nginx-build -c config/configure.example -m config/modules.cfg.example -d work -clear 8 | 9 | check: 10 | go test ./... 11 | 12 | fmt: 13 | go fmt ./... 14 | 15 | clean: 16 | rm -rf nginx-build 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-build 2 | 3 | `nginx-build` - provides a command to build nginx seamlessly. 4 | 5 | ![gif](https://raw.githubusercontent.com/cubicdaiya/nginx-build/master/images/nginx-build.gif) 6 | 7 | ## Requirements 8 | 9 | * [git](https://git-scm.com/) and [hg](https://www.mercurial-scm.org/) for downloading 3rd party modules 10 | * [patch](https://savannah.gnu.org/projects/patch/) for applying patch to nginx 11 | 12 | ## Build Support 13 | 14 | * [nginx](https://nginx.org/) 15 | * [OpenResty](https://openresty.org/) 16 | * [freenginx](https://freenginx.org/) 17 | 18 | ## Installation 19 | 20 | ```bash 21 | go install github.com/cubicdaiya/nginx-build@latest 22 | ``` 23 | 24 | ## Quick Start 25 | 26 | ```console 27 | nginx-build -d work 28 | ``` 29 | 30 | ## Custom Configuration 31 | 32 | `nginx-build` provides a mechanism for customizing configuration for building nginx. 33 | 34 | ### Configuration for building nginx 35 | 36 | Prepare a configure script like the following. 37 | 38 | ```bash 39 | #!/bin/sh 40 | 41 | ./configure \ 42 | --sbin-path=/usr/sbin/nginx \ 43 | --conf-path=/etc/nginx/nginx.conf \ 44 | ``` 45 | 46 | Give this file to `nginx-build` with `-c`. 47 | 48 | ```bash 49 | $ nginx-build -d work -c configure.example 50 | ``` 51 | 52 | ### Embedding zlib statically 53 | 54 | Give `-zlib` to `nginx-build`. 55 | 56 | ```bash 57 | $ nginx-build -d work -zlib 58 | ``` 59 | 60 | `-zlibversion` is an option to set a version of zlib. 61 | 62 | ### Embedding PCRE statically 63 | 64 | Give `-pcre` to `nginx-build`. 65 | 66 | ```bash 67 | $ nginx-build -d work -pcre 68 | ``` 69 | 70 | `-pcreversion` is an option to set a version of PCRE. 71 | 72 | ### Embedding OpenSSL statically 73 | 74 | Give `-openssl` to `nginx-build`. 75 | 76 | ```bash 77 | $ nginx-build -d work -openssl 78 | ``` 79 | 80 | `-opensslversion` is an option to set a version of OpenSSL. 81 | 82 | ### Embedding LibreSSL statically 83 | 84 | Give `-libressl` to `nginx-build`. 85 | 86 | ```bash 87 | $ nginx-build -d work -libressl 88 | ``` 89 | 90 | `-libresslversion` is an option to set a version of LibreSSL. 91 | 92 | ### Embedding 3rd-party modules 93 | 94 | `nginx-build` provides a mechanism for embedding 3rd-party modules. 95 | Prepare a json file below. 96 | 97 | ```ini 98 | [ 99 | { 100 | "name": "ngx_http_hello_world", 101 | "form": "git", 102 | "url": "https://github.com/cubicdaiya/ngx_http_hello_world" 103 | } 104 | ] 105 | ``` 106 | 107 | Give this file to `nginx-build` with `-m`. 108 | 109 | ```bash 110 | $ nginx-build -d work -m modules.json.example 111 | ``` 112 | 113 | #### Embedding 3rd-party module dynamically 114 | 115 | Give `true` to `dynamic`. 116 | 117 | ```ini 118 | [ 119 | { 120 | "name": "ngx_http_hello_world", 121 | "form": "git", 122 | "url": "https://github.com/cubicdaiya/ngx_http_hello_world", 123 | "dynamic": true 124 | } 125 | ] 126 | ``` 127 | 128 | #### Provision for 3rd-party module 129 | 130 | There are some 3rd-party modules expected provision. `nginx-build` provides the options such as `shprov` and `shprovdir` for this problem. 131 | There is the example configuration below. 132 | 133 | ```ini 134 | [ 135 | { 136 | "name": "njs/nginx", 137 | "form": "hg", 138 | "url": "https://hg.nginx.org/njs", 139 | "shprov": "./configure && make", 140 | "shprovdir": ".." 141 | } 142 | ] 143 | ``` 144 | 145 | ## Applying patch before building nginx 146 | 147 | `nginx-build` provides the options such as `-patch` and `-patch-opt` for applying patch to nginx. 148 | 149 | ```console 150 | nginx-build \ 151 | -d work \ 152 | -patch something.patch \ 153 | -patch-opt "-p1" 154 | ``` 155 | 156 | ## Idempotent build 157 | 158 | `nginx-build` supports a certain level of idempotent build of nginx. 159 | If you want to ensure a build of nginx idempotent and do not want to build nginx as same as already installed nginx, 160 | give `-idempotent` to `nginx-build`. 161 | 162 | ```bash 163 | $ nginx-build -d work -idempotent 164 | ``` 165 | 166 | `-idempotent` ensures an idempotent by checking the software versions below. 167 | 168 | * nginx 169 | * PCRE 170 | * zlib 171 | * OpenSSL 172 | 173 | On the other hand, `-idempotent` does not cover versions of 3rd party modules and dynamic linked libraries. 174 | 175 | ## Build OpenResty 176 | 177 | `nginx-build` supports to build [OpenResty](https://openresty.org/). 178 | 179 | ```bash 180 | $ nginx-build -d work -openresty -pcre -openssl 181 | ``` 182 | 183 | If you don't install PCRE and OpenSSL on your system, it is required to add the option `-pcre` and `-openssl`. 184 | 185 | 186 | And there is the limitation for the support of OpenResty. 187 | `nginx-build` does not allow to use OpenResty's unique configure options directly. 188 | If you want to use OpenResty's unique configure option, [Configuration for building nginx](#configuration-for-building-nginx) is helpful. 189 | 190 | ## Build freenginx 191 | 192 | `nginx-build` supports to build [freenginx](https://freenginx.org/). 193 | 194 | ```bash 195 | $ nginx-build -d work -freenginx -openssl 196 | ``` 197 | 198 | If you don't install OpenSSL on your system, it is required to add the option `-openssl`. 199 | -------------------------------------------------------------------------------- /builder/builder.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | "strings" 8 | 9 | "github.com/cubicdaiya/nginx-build/command" 10 | "github.com/cubicdaiya/nginx-build/openresty" 11 | ) 12 | 13 | type Builder struct { 14 | Version string 15 | DownloadURLPrefix string 16 | Component int 17 | // for dependencies such as pcre and zlib and openssl 18 | Static bool 19 | } 20 | 21 | var ( 22 | nginxVersionRe *regexp.Regexp 23 | pcreVersionRe *regexp.Regexp 24 | zlibVersionRe *regexp.Regexp 25 | opensslVersionRe *regexp.Regexp 26 | libresslVersionRe *regexp.Regexp 27 | openrestyVersionRe *regexp.Regexp 28 | freenginxVersionRe *regexp.Regexp 29 | ) 30 | 31 | func init() { 32 | nginxVersionRe = regexp.MustCompile(`nginx version: nginx.(\d+\.\d+\.\d+)`) 33 | pcreVersionRe = regexp.MustCompile(`--with-pcre=.+/pcre-(\d+\.\d+)`) 34 | zlibVersionRe = regexp.MustCompile(`--with-zlib=.+/zlib-(\d+\.\d+\.\d+)`) 35 | opensslVersionRe = regexp.MustCompile(`--with-openssl=.+/openssl-(\d+\.\d+\.\d+[a-z]*)`) 36 | libresslVersionRe = regexp.MustCompile(`--with-openssl=.+/libressl-(\d+\.\d+\.\d+)`) 37 | openrestyVersionRe = regexp.MustCompile(`nginx version: openresty/(\d+\.\d+\.\d+\.\d+)`) 38 | freenginxVersionRe = regexp.MustCompile(`freenginx version: freenginx/(\d+\.\d+\.\d+)`) 39 | } 40 | 41 | func (builder *Builder) name() string { 42 | var name string 43 | switch builder.Component { 44 | case ComponentNginx: 45 | name = "nginx" 46 | case ComponentPcre: 47 | name = "pcre2" 48 | case ComponentOpenSSL: 49 | name = "openssl" 50 | case ComponentLibreSSL: 51 | name = "libressl" 52 | case ComponentZlib: 53 | name = "zlib" 54 | case ComponentOpenResty: 55 | name = openresty.Name(builder.Version) 56 | case ComponentFreenginx: 57 | name = "freenginx" 58 | default: 59 | panic("invalid component") 60 | } 61 | return name 62 | } 63 | 64 | func (builder *Builder) option() string { 65 | name := builder.name() 66 | 67 | // libressl does not match option name 68 | if name == "libressl" { 69 | name = "openssl" 70 | } 71 | 72 | // pcre2 does not match option name 73 | if name == "pcre2" { 74 | name = "pcre" 75 | } 76 | 77 | return fmt.Sprintf("--with-%s", name) 78 | } 79 | 80 | func (builder *Builder) DownloadURL() string { 81 | switch builder.Component { 82 | case ComponentNginx: 83 | return fmt.Sprintf("%s/nginx-%s.tar.gz", NginxDownloadURLPrefix, builder.Version) 84 | case ComponentPcre: 85 | return fmt.Sprintf("%s/pcre2-%s/pcre2-%s.tar.gz", PcreDownloadURLPrefix, builder.Version, builder.Version) 86 | case ComponentOpenSSL: 87 | return fmt.Sprintf("%s/openssl-%s/openssl-%s.tar.gz", OpenSSLDownloadURLPrefix, builder.Version, builder.Version) 88 | case ComponentLibreSSL: 89 | return fmt.Sprintf("%s/libressl-%s.tar.gz", LibreSSLDownloadURLPrefix, builder.Version) 90 | case ComponentZlib: 91 | return fmt.Sprintf("%s/zlib-%s.tar.gz", ZlibDownloadURLPrefix, builder.Version) 92 | case ComponentOpenResty: 93 | return fmt.Sprintf("%s/openresty-%s.tar.gz", OpenRestyDownloadURLPrefix, builder.Version) 94 | case ComponentFreenginx: 95 | return fmt.Sprintf("%s/freenginx-%s.tar.gz", FreenginxDownloadURLPrefix, builder.Version) 96 | default: 97 | panic("invalid component") 98 | } 99 | } 100 | 101 | func (builder *Builder) SourcePath() string { 102 | return fmt.Sprintf("%s-%s", builder.name(), builder.Version) 103 | } 104 | 105 | func (builder *Builder) ArchivePath() string { 106 | return fmt.Sprintf("%s.tar.gz", builder.SourcePath()) 107 | } 108 | 109 | func (builder *Builder) LogPath() string { 110 | return fmt.Sprintf("%s-%s.log", builder.name(), builder.Version) 111 | } 112 | 113 | func (builder *Builder) IsIncludeWithOption(nginxConfigure string) bool { 114 | if strings.Contains(nginxConfigure, builder.option()+"=") { 115 | return true 116 | } 117 | return false 118 | } 119 | 120 | func (builder *Builder) WarnMsgWithLibrary() string { 121 | return fmt.Sprintf("[warn]Using '%s' is discouraged. Instead give '-%s' and '-%sversion' to 'nginx-build'", 122 | builder.option(), builder.name(), builder.name()) 123 | } 124 | 125 | func (builder *Builder) InstalledVersion() (string, error) { 126 | nginxBinPath := "/usr/local/sbin/nginx" 127 | if os.Getenv("NGINX_BIN") != "" { 128 | nginxBinPath = os.Getenv("NGINX_BIN") 129 | } 130 | args := []string{nginxBinPath, "-V"} 131 | cmd, err := command.Make(args) 132 | if err != nil { 133 | return "", err 134 | } 135 | 136 | result, err := cmd.CombinedOutput() 137 | if err != nil { 138 | return "", err 139 | } 140 | 141 | openRestyName := openresty.Name(builder.Version) 142 | var versionRe *regexp.Regexp 143 | 144 | switch builder.name() { 145 | case "nginx": 146 | versionRe = nginxVersionRe 147 | case openRestyName: 148 | versionRe = openrestyVersionRe 149 | case "zlib": 150 | versionRe = zlibVersionRe 151 | case "pcre2": 152 | versionRe = pcreVersionRe 153 | case "openssl": 154 | versionRe = opensslVersionRe 155 | case "libressl": 156 | versionRe = libresslVersionRe 157 | case "freenginx": 158 | versionRe = freenginxVersionRe 159 | } 160 | 161 | m := versionRe.FindSubmatch(result) 162 | if len(m) < 2 { 163 | return "", nil 164 | } 165 | return string(m[1]), nil 166 | } 167 | 168 | func MakeBuilder(component int, version string) Builder { 169 | var builder Builder 170 | builder.Component = component 171 | builder.Version = version 172 | switch component { 173 | case ComponentNginx: 174 | builder.DownloadURLPrefix = NginxDownloadURLPrefix 175 | case ComponentPcre: 176 | builder.DownloadURLPrefix = fmt.Sprintf("%s/pcre2-%s", PcreDownloadURLPrefix, version) 177 | case ComponentOpenSSL: 178 | builder.DownloadURLPrefix = fmt.Sprintf("%s/openssl-%s", OpenSSLDownloadURLPrefix, version) 179 | case ComponentLibreSSL: 180 | builder.DownloadURLPrefix = LibreSSLDownloadURLPrefix 181 | case ComponentZlib: 182 | builder.DownloadURLPrefix = ZlibDownloadURLPrefix 183 | case ComponentOpenResty: 184 | builder.DownloadURLPrefix = OpenRestyDownloadURLPrefix 185 | case ComponentFreenginx: 186 | builder.DownloadURLPrefix = FreenginxDownloadURLPrefix 187 | default: 188 | panic("invalid component") 189 | } 190 | return builder 191 | } 192 | 193 | func MakeLibraryBuilder(component int, version string, static bool) Builder { 194 | builder := MakeBuilder(component, version) 195 | builder.Static = static 196 | return builder 197 | } 198 | -------------------------------------------------------------------------------- /builder/builder_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func setupBuilders(t *testing.T) []Builder { 9 | builders := make([]Builder, ComponentMax) 10 | builders[ComponentNginx] = MakeBuilder(ComponentNginx, NginxVersion) 11 | builders[ComponentPcre] = MakeLibraryBuilder(ComponentPcre, PcreVersion, false) 12 | builders[ComponentOpenSSL] = MakeLibraryBuilder(ComponentOpenSSL, OpenSSLVersion, true) 13 | builders[ComponentLibreSSL] = MakeLibraryBuilder(ComponentLibreSSL, LibreSSLVersion, true) 14 | builders[ComponentZlib] = MakeLibraryBuilder(ComponentZlib, ZlibVersion, false) 15 | builders[ComponentOpenResty] = MakeBuilder(ComponentOpenResty, OpenRestyVersion) 16 | builders[ComponentFreenginx] = MakeBuilder(ComponentFreenginx, FreenginxVersion) 17 | return builders 18 | } 19 | 20 | func TestName(t *testing.T) { 21 | builders := setupBuilders(t) 22 | 23 | tests := []struct { 24 | got string 25 | want string 26 | }{ 27 | { 28 | got: builders[ComponentNginx].name(), 29 | want: "nginx", 30 | }, 31 | { 32 | got: builders[ComponentPcre].name(), 33 | want: "pcre2", 34 | }, 35 | { 36 | got: builders[ComponentOpenSSL].name(), 37 | want: "openssl", 38 | }, 39 | { 40 | got: builders[ComponentLibreSSL].name(), 41 | want: "libressl", 42 | }, 43 | { 44 | got: builders[ComponentZlib].name(), 45 | want: "zlib", 46 | }, 47 | { 48 | got: builders[ComponentOpenResty].name(), 49 | want: "openresty", 50 | }, 51 | { 52 | got: builders[ComponentFreenginx].name(), 53 | want: "freenginx", 54 | }, 55 | } 56 | 57 | for _, test := range tests { 58 | if test.got != test.want { 59 | t.Fatalf("got: %v, want: %v", test.got, test.want) 60 | } 61 | } 62 | } 63 | 64 | func TestOption(t *testing.T) { 65 | builders := setupBuilders(t) 66 | 67 | tests := []struct { 68 | got string 69 | want string 70 | }{ 71 | { 72 | got: builders[ComponentPcre].option(), 73 | want: "--with-pcre", 74 | }, 75 | { 76 | got: builders[ComponentOpenSSL].option(), 77 | want: "--with-openssl", 78 | }, 79 | { 80 | got: builders[ComponentLibreSSL].option(), 81 | want: "--with-openssl", 82 | }, 83 | { 84 | got: builders[ComponentZlib].option(), 85 | want: "--with-zlib", 86 | }, 87 | } 88 | 89 | for _, test := range tests { 90 | if test.got != test.want { 91 | t.Fatalf("got: %v, want: %v", test.got, test.want) 92 | } 93 | } 94 | } 95 | 96 | func TestDownloadURL(t *testing.T) { 97 | builders := setupBuilders(t) 98 | 99 | tests := []struct { 100 | got string 101 | want string 102 | }{ 103 | { 104 | got: builders[ComponentNginx].DownloadURL(), 105 | want: fmt.Sprintf("%s/nginx-%s.tar.gz", NginxDownloadURLPrefix, NginxVersion), 106 | }, 107 | { 108 | got: builders[ComponentPcre].DownloadURL(), 109 | want: fmt.Sprintf("%s/pcre2-%s/pcre2-%s.tar.gz", PcreDownloadURLPrefix, PcreVersion, PcreVersion), 110 | }, 111 | { 112 | got: builders[ComponentOpenSSL].DownloadURL(), 113 | want: fmt.Sprintf("%s/openssl-%s/openssl-%s.tar.gz", OpenSSLDownloadURLPrefix, OpenSSLVersion, OpenSSLVersion), 114 | }, 115 | { 116 | got: builders[ComponentLibreSSL].DownloadURL(), 117 | want: fmt.Sprintf("%s/libressl-%s.tar.gz", LibreSSLDownloadURLPrefix, LibreSSLVersion), 118 | }, 119 | { 120 | got: builders[ComponentZlib].DownloadURL(), 121 | want: fmt.Sprintf("%s/zlib-%s.tar.gz", ZlibDownloadURLPrefix, ZlibVersion), 122 | }, 123 | { 124 | got: builders[ComponentOpenResty].DownloadURL(), 125 | want: fmt.Sprintf("%s/openresty-%s.tar.gz", OpenRestyDownloadURLPrefix, OpenRestyVersion), 126 | }, 127 | { 128 | got: builders[ComponentFreenginx].DownloadURL(), 129 | want: fmt.Sprintf("%s/freenginx-%s.tar.gz", FreenginxDownloadURLPrefix, FreenginxVersion), 130 | }, 131 | } 132 | 133 | for _, test := range tests { 134 | if test.got != test.want { 135 | t.Fatalf("got: %v, want: %v", test.got, test.want) 136 | } 137 | } 138 | } 139 | 140 | func TestSourcePath(t *testing.T) { 141 | builders := setupBuilders(t) 142 | 143 | tests := []struct { 144 | got string 145 | want string 146 | }{ 147 | { 148 | got: builders[ComponentNginx].SourcePath(), 149 | want: fmt.Sprintf("nginx-%s", NginxVersion), 150 | }, 151 | { 152 | got: builders[ComponentPcre].SourcePath(), 153 | want: fmt.Sprintf("pcre2-%s", PcreVersion), 154 | }, 155 | { 156 | got: builders[ComponentOpenSSL].SourcePath(), 157 | want: fmt.Sprintf("openssl-%s", OpenSSLVersion), 158 | }, 159 | { 160 | got: builders[ComponentLibreSSL].SourcePath(), 161 | want: fmt.Sprintf("libressl-%s", LibreSSLVersion), 162 | }, 163 | { 164 | got: builders[ComponentZlib].SourcePath(), 165 | want: fmt.Sprintf("zlib-%s", ZlibVersion), 166 | }, 167 | { 168 | got: builders[ComponentOpenResty].SourcePath(), 169 | want: fmt.Sprintf("openresty-%s", OpenRestyVersion), 170 | }, 171 | { 172 | got: builders[ComponentFreenginx].SourcePath(), 173 | want: fmt.Sprintf("freenginx-%s", FreenginxVersion), 174 | }, 175 | } 176 | 177 | for _, test := range tests { 178 | if test.got != test.want { 179 | t.Fatalf("got: %v, want: %v", test.got, test.want) 180 | } 181 | } 182 | } 183 | 184 | func TestArchivePath(t *testing.T) { 185 | builders := setupBuilders(t) 186 | 187 | tests := []struct { 188 | got string 189 | want string 190 | }{ 191 | { 192 | got: builders[ComponentNginx].ArchivePath(), 193 | want: fmt.Sprintf("nginx-%s.tar.gz", NginxVersion), 194 | }, 195 | { 196 | got: builders[ComponentPcre].ArchivePath(), 197 | want: fmt.Sprintf("pcre2-%s.tar.gz", PcreVersion), 198 | }, 199 | { 200 | got: builders[ComponentOpenSSL].ArchivePath(), 201 | want: fmt.Sprintf("openssl-%s.tar.gz", OpenSSLVersion), 202 | }, 203 | { 204 | got: builders[ComponentLibreSSL].ArchivePath(), 205 | want: fmt.Sprintf("libressl-%s.tar.gz", LibreSSLVersion), 206 | }, 207 | { 208 | got: builders[ComponentZlib].ArchivePath(), 209 | want: fmt.Sprintf("zlib-%s.tar.gz", ZlibVersion), 210 | }, 211 | { 212 | got: builders[ComponentOpenResty].ArchivePath(), 213 | want: fmt.Sprintf("openresty-%s.tar.gz", OpenRestyVersion), 214 | }, 215 | { 216 | got: builders[ComponentFreenginx].ArchivePath(), 217 | want: fmt.Sprintf("freenginx-%s.tar.gz", FreenginxVersion), 218 | }, 219 | } 220 | 221 | for _, test := range tests { 222 | if test.got != test.want { 223 | t.Fatalf("got: %v, want: %v", test.got, test.want) 224 | } 225 | } 226 | } 227 | 228 | func TestLogPath(t *testing.T) { 229 | builders := setupBuilders(t) 230 | 231 | tests := []struct { 232 | got string 233 | want string 234 | }{ 235 | { 236 | got: builders[ComponentNginx].LogPath(), 237 | want: fmt.Sprintf("nginx-%s.log", NginxVersion), 238 | }, 239 | { 240 | got: builders[ComponentPcre].LogPath(), 241 | want: fmt.Sprintf("pcre2-%s.log", PcreVersion), 242 | }, 243 | { 244 | got: builders[ComponentOpenSSL].LogPath(), 245 | want: fmt.Sprintf("openssl-%s.log", OpenSSLVersion), 246 | }, 247 | { 248 | got: builders[ComponentLibreSSL].LogPath(), 249 | want: fmt.Sprintf("libressl-%s.log", LibreSSLVersion), 250 | }, 251 | { 252 | got: builders[ComponentZlib].LogPath(), 253 | want: fmt.Sprintf("zlib-%s.log", ZlibVersion), 254 | }, 255 | { 256 | got: builders[ComponentOpenResty].LogPath(), 257 | want: fmt.Sprintf("openresty-%s.log", OpenRestyVersion), 258 | }, 259 | { 260 | got: builders[ComponentFreenginx].LogPath(), 261 | want: fmt.Sprintf("freenginx-%s.log", FreenginxVersion), 262 | }, 263 | } 264 | 265 | for _, test := range tests { 266 | if test.got != test.want { 267 | t.Fatalf("got: %v, want: %v", test.got, test.want) 268 | } 269 | } 270 | } 271 | 272 | func TestLibrary(t *testing.T) { 273 | builders := setupBuilders(t) 274 | 275 | tests := []struct { 276 | got bool 277 | want bool 278 | }{ 279 | { 280 | got: builders[ComponentPcre].Static, 281 | want: false, 282 | }, 283 | { 284 | got: builders[ComponentOpenSSL].Static, 285 | want: true, 286 | }, 287 | { 288 | got: builders[ComponentLibreSSL].Static, 289 | want: true, 290 | }, 291 | { 292 | got: builders[ComponentZlib].Static, 293 | want: false, 294 | }, 295 | } 296 | 297 | for _, test := range tests { 298 | if test.got != test.want { 299 | t.Fatalf("got: %v, want: %v", test.got, test.want) 300 | } 301 | } 302 | } 303 | 304 | func TestMakeStaticLibrary(t *testing.T) { 305 | builders := setupBuilders(t) 306 | 307 | tests := []struct { 308 | builder Builder 309 | staticLibrary StaticLibrary 310 | version string 311 | }{ 312 | { 313 | builder: builders[ComponentPcre], 314 | staticLibrary: MakeStaticLibrary(&builders[ComponentPcre]), 315 | version: PcreVersion, 316 | }, 317 | { 318 | builder: builders[ComponentOpenSSL], 319 | staticLibrary: MakeStaticLibrary(&builders[ComponentOpenSSL]), 320 | version: OpenSSLVersion, 321 | }, 322 | { 323 | builder: builders[ComponentLibreSSL], 324 | staticLibrary: MakeStaticLibrary(&builders[ComponentLibreSSL]), 325 | version: LibreSSLVersion, 326 | }, 327 | { 328 | builder: builders[ComponentZlib], 329 | staticLibrary: MakeStaticLibrary(&builders[ComponentZlib]), 330 | version: ZlibVersion, 331 | }, 332 | } 333 | 334 | for _, test := range tests { 335 | if test.builder.name() != test.staticLibrary.Name { 336 | t.Fatalf("not equal name() between builder(%v) and static library(%v)", test.builder.name(), test.staticLibrary.Name) 337 | } 338 | if test.builder.option() != test.staticLibrary.Option { 339 | t.Fatalf("not equal option() between builder(%v) and static library(%v)", test.builder.option(), test.staticLibrary.Option) 340 | } 341 | if test.builder.Version != test.version { 342 | t.Fatalf("not equal version between builder's and default's") 343 | } 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /builder/const.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | // nginx 4 | const ( 5 | NginxVersion = "1.28.0" 6 | NginxDownloadURLPrefix = "https://nginx.org/download" 7 | ) 8 | 9 | // pcre 10 | const ( 11 | PcreVersion = "10.45" 12 | PcreDownloadURLPrefix = "https://github.com/PCRE2Project/pcre2/releases/download" 13 | ) 14 | 15 | // openssl 16 | const ( 17 | OpenSSLVersion = "3.5.0" 18 | OpenSSLDownloadURLPrefix = "https://github.com/openssl/openssl/releases/download" 19 | ) 20 | 21 | // libressl 22 | const ( 23 | LibreSSLVersion = "4.1.0" 24 | LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL" 25 | ) 26 | 27 | // zlib 28 | const ( 29 | ZlibVersion = "1.3.1" 30 | ZlibDownloadURLPrefix = "https://zlib.net" 31 | ) 32 | 33 | // openResty 34 | const ( 35 | OpenRestyVersion = "1.27.1.2" 36 | OpenRestyDownloadURLPrefix = "https://openresty.org/download" 37 | ) 38 | 39 | // freenginx 40 | const ( 41 | FreenginxVersion = "1.28.0" 42 | FreenginxDownloadURLPrefix = "https://freenginx.org/download" 43 | ) 44 | 45 | // component enumerations 46 | const ( 47 | ComponentNginx = iota 48 | ComponentOpenResty 49 | ComponentFreenginx 50 | ComponentPcre 51 | ComponentOpenSSL 52 | ComponentLibreSSL 53 | ComponentZlib 54 | ComponentMax 55 | ) 56 | -------------------------------------------------------------------------------- /builder/nginx.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strconv" 7 | 8 | "github.com/cubicdaiya/nginx-build/command" 9 | ) 10 | 11 | func BuildNginx(jobs int) error { 12 | args := []string{"make", "-j", strconv.Itoa(jobs)} 13 | if command.VerboseEnabled { 14 | return command.Run(args) 15 | } 16 | 17 | f, err := os.Create("nginx-build.log") 18 | if err != nil { 19 | return command.Run(args) 20 | } 21 | defer f.Close() 22 | 23 | cmd, err := command.Make(args) 24 | if err != nil { 25 | return err 26 | } 27 | writer := bufio.NewWriter(f) 28 | cmd.Stderr = writer 29 | defer writer.Flush() 30 | 31 | return cmd.Run() 32 | } 33 | 34 | func IsSameVersion(builders []Builder) (bool, error) { 35 | sameVersion := true 36 | for _, b := range builders { 37 | vi, err := b.InstalledVersion() 38 | if err != nil { 39 | return false, err 40 | } 41 | switch b.Component { 42 | case ComponentPcre: 43 | fallthrough 44 | case ComponentOpenSSL: 45 | fallthrough 46 | case ComponentLibreSSL: 47 | fallthrough 48 | case ComponentZlib: 49 | if vi == "" && !b.Static { 50 | continue 51 | } else if vi == b.Version && b.Static { 52 | continue 53 | } 54 | default: 55 | if vi == b.Version { 56 | continue 57 | } 58 | } 59 | sameVersion = false 60 | } 61 | 62 | if sameVersion { 63 | return true, nil 64 | } 65 | 66 | return false, nil 67 | } 68 | -------------------------------------------------------------------------------- /builder/staticlibrary.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | type StaticLibrary struct { 4 | Name string 5 | Version string 6 | Option string 7 | } 8 | 9 | func MakeStaticLibrary(builder *Builder) StaticLibrary { 10 | return StaticLibrary{ 11 | Name: builder.name(), 12 | Version: builder.Version, 13 | Option: builder.option()} 14 | } 15 | -------------------------------------------------------------------------------- /command/command.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "os/exec" 7 | ) 8 | 9 | var VerboseEnabled bool 10 | 11 | func checkVerboseEnabled(cmd *exec.Cmd) { 12 | if VerboseEnabled { 13 | cmd.Stdout = os.Stdout 14 | cmd.Stderr = os.Stderr 15 | } 16 | } 17 | 18 | func Make(args []string) (*exec.Cmd, error) { 19 | var cmd *exec.Cmd 20 | switch len(args) { 21 | case 0: 22 | return nil, errors.New("empty command") 23 | case 1: 24 | cmd = exec.Command(args[0]) 25 | default: 26 | cmd = exec.Command(args[0], args[1:]...) 27 | } 28 | 29 | return cmd, nil 30 | } 31 | 32 | func Run(args []string) error { 33 | cmd, err := Make(args) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | checkVerboseEnabled(cmd) 39 | return cmd.Run() 40 | } 41 | -------------------------------------------------------------------------------- /config/configure.example: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./configure \ 4 | --sbin-path=/usr/sbin/nginx \ 5 | --conf-path=/etc/nginx/nginx.conf \ 6 | -------------------------------------------------------------------------------- /config/modules.json.brotli: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "ngx_brotli", 4 | "form": "git", 5 | "url": "https://github.com/google/ngx_brotli.git", 6 | "shprov": "cd deps/brotli && mkdir -p out && cd out && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_FLAGS=\"-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections\" -DCMAKE_CXX_FLAGS=\"-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections\" -DCMAKE_INSTALL_PREFIX=./installed .. && cmake --build . --config Release --target brotlienc", 7 | "shprovdir": "." 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /config/modules.json.example: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "ngx_http_hello_world", 4 | "form": "git", 5 | "url": "https://github.com/cubicdaiya/ngx_http_hello_world" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /config/modules.json.njs: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "njs/nginx", 4 | "form": "hg", 5 | "url": "https://hg.nginx.org/njs", 6 | "shprov": "./configure && make", 7 | "shprovdir": ".." 8 | } 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /configure/configure.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | 7 | "github.com/cubicdaiya/nginx-build/command" 8 | ) 9 | 10 | func Run() error { 11 | args := []string{"sh", "./nginx-configure"} 12 | if command.VerboseEnabled { 13 | return command.Run(args) 14 | } 15 | 16 | f, err := os.Create("nginx-configure.log") 17 | if err != nil { 18 | return command.Run(args) 19 | } 20 | defer f.Close() 21 | 22 | cmd, err := command.Make(args) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | writer := bufio.NewWriter(f) 28 | cmd.Stdout = writer 29 | defer writer.Flush() 30 | 31 | return cmd.Run() 32 | } 33 | -------------------------------------------------------------------------------- /configure/configure_test.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/cubicdaiya/nginx-build/builder" 9 | "github.com/cubicdaiya/nginx-build/module3rd" 10 | ) 11 | 12 | func setupBuilders(t *testing.T) []builder.Builder { 13 | builders := make([]builder.Builder, builder.ComponentMax) 14 | builders[builder.ComponentPcre] = builder.MakeBuilder(builder.ComponentPcre, builder.PcreVersion) 15 | builders[builder.ComponentOpenSSL] = builder.MakeBuilder(builder.ComponentOpenSSL, builder.OpenSSLVersion) 16 | builders[builder.ComponentZlib] = builder.MakeBuilder(builder.ComponentZlib, builder.ZlibVersion) 17 | return builders 18 | } 19 | 20 | func setupModules3rd(t *testing.T) []module3rd.Module3rd { 21 | modules3rdConf := "../config/modules.json.example" 22 | modules3rd, err := module3rd.Load(modules3rdConf) 23 | if err != nil { 24 | t.Fatalf("Failed to load %s\n", modules3rdConf) 25 | } 26 | return modules3rd 27 | } 28 | 29 | func TestConfiguregenModule3rd(t *testing.T) { 30 | modules3rd := setupModules3rd(t) 31 | 32 | configureModules3rd := generateForModule3rd(modules3rd) 33 | 34 | wantedOptions := []string{ 35 | "-add-module=../ngx_http_hello_world", 36 | } 37 | 38 | for _, want := range wantedOptions { 39 | if !strings.Contains(configureModules3rd, want) { 40 | t.Fatalf("configure string does not contain wanted option: %v", want) 41 | } 42 | } 43 | } 44 | 45 | func TestConfiguregenWithStaticLibraries(t *testing.T) { 46 | 47 | builders := setupBuilders(t) 48 | 49 | var dependencies []builder.StaticLibrary 50 | dependencies = append(dependencies, builder.MakeStaticLibrary(&builders[builder.ComponentPcre])) 51 | dependencies = append(dependencies, builder.MakeStaticLibrary(&builders[builder.ComponentOpenSSL])) 52 | dependencies = append(dependencies, builder.MakeStaticLibrary(&builders[builder.ComponentZlib])) 53 | var configureOptions Options 54 | configureScript := Generate("", []module3rd.Module3rd{}, dependencies, configureOptions, "", false, 1) 55 | 56 | wantedOptions := []string{ 57 | "--with-http_ssl_module", 58 | fmt.Sprintf("--with-pcre=../pcre2-%s \\\n", builder.PcreVersion), 59 | fmt.Sprintf("--with-openssl=../openssl-%s \\\n", builder.OpenSSLVersion), 60 | fmt.Sprintf("--with-zlib=../zlib-%s \\\n", builder.ZlibVersion), 61 | } 62 | 63 | for _, want := range wantedOptions { 64 | if !strings.Contains(configureScript, want) { 65 | t.Fatalf("configure script does not contain wanted option: %v", want) 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /configure/configureopt.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | type Options struct { 4 | Values map[string]OptionValue 5 | Bools map[string]OptionBool 6 | } 7 | 8 | type OptionValue struct { 9 | Name string 10 | Desc string 11 | Value *string 12 | } 13 | 14 | type OptionBool struct { 15 | Name string 16 | Desc string 17 | Enabled *bool 18 | } 19 | 20 | func MakeArgsBool() map[string]OptionBool { 21 | return make(map[string]OptionBool) 22 | } 23 | 24 | func MakeArgsString() map[string]OptionValue { 25 | return make(map[string]OptionValue) 26 | } 27 | -------------------------------------------------------------------------------- /configure/generate.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/cubicdaiya/nginx-build/builder" 8 | "github.com/cubicdaiya/nginx-build/module3rd" 9 | ) 10 | 11 | func Generate(configure string, modules3rd []module3rd.Module3rd, dependencies []builder.StaticLibrary, options Options, rootDir string, openResty bool, jobs int) string { 12 | openSSLStatic := false 13 | if len(configure) == 0 { 14 | configure = `#!/bin/sh 15 | 16 | ./configure \ 17 | ` 18 | } 19 | 20 | if openResty { 21 | configure += fmt.Sprintf("-j%d \\\n", jobs) 22 | } 23 | 24 | for _, d := range dependencies { 25 | configure += fmt.Sprintf("%s=../%s-%s \\\n", d.Option, d.Name, d.Version) 26 | if d.Name == "openssl" || d.Name == "libressl" { 27 | openSSLStatic = true 28 | } 29 | } 30 | 31 | if openSSLStatic && !strings.Contains(configure, "--with-http_ssl_module") { 32 | configure += "--with-http_ssl_module \\\n" 33 | } 34 | 35 | configure_modules3rd := generateForModule3rd(modules3rd) 36 | configure += configure_modules3rd 37 | 38 | for _, option := range options.Values { 39 | if *option.Value != "" { 40 | if option.Name == "--add-module" { 41 | configure += normalizeAddModulePaths(*option.Value, rootDir, false) 42 | } else if option.Name == "--add-dynamic-module" { 43 | configure += normalizeAddModulePaths(*option.Value, rootDir, true) 44 | } else { 45 | if strings.Contains(*option.Value, " ") { 46 | configure += option.Name + "=" + "'" + *option.Value + "'" + " \\\n" 47 | } else { 48 | configure += option.Name + "=" + *option.Value + " \\\n" 49 | } 50 | } 51 | } 52 | } 53 | 54 | for _, option := range options.Bools { 55 | if *option.Enabled { 56 | configure += option.Name + " \\\n" 57 | } 58 | } 59 | 60 | return configure 61 | } 62 | 63 | func generateForModule3rd(modules3rd []module3rd.Module3rd) string { 64 | result := "" 65 | for _, m := range modules3rd { 66 | opt := "--add-module" 67 | if m.Dynamic { 68 | opt = "--add-dynamic-module" 69 | } 70 | if m.Form == "local" { 71 | result += fmt.Sprintf("%s=%s \\\n", opt, m.Url) 72 | } else { 73 | result += fmt.Sprintf("%s=../%s \\\n", opt, m.Name) 74 | } 75 | } 76 | return result 77 | } 78 | -------------------------------------------------------------------------------- /configure/normalize.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func Normalize(configure string) string { 9 | configure = strings.TrimRight(configure, "\n") 10 | configure = strings.TrimRight(configure, " ") 11 | configure = strings.TrimRight(configure, "\\") 12 | if configure != "" { 13 | configure += " " 14 | } 15 | return configure 16 | } 17 | 18 | func normalizeAddModulePaths(path, rootDir string, dynamic bool) string { 19 | var result string 20 | if len(path) == 0 { 21 | return path 22 | } 23 | 24 | module_paths := strings.Split(path, ",") 25 | 26 | opt := "--add-module" 27 | if dynamic { 28 | opt = "--add-dynamic-module" 29 | } 30 | 31 | for _, module_path := range module_paths { 32 | if strings.HasPrefix(module_path, "/") { 33 | result += fmt.Sprintf("%s=%s \\\n", opt, module_path) 34 | } else { 35 | result += fmt.Sprintf("%s=%s/%s \\\n", opt, rootDir, module_path) 36 | } 37 | } 38 | 39 | return result 40 | } 41 | -------------------------------------------------------------------------------- /download.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | "os" 9 | "time" 10 | 11 | "github.com/cubicdaiya/nginx-build/builder" 12 | "github.com/cubicdaiya/nginx-build/command" 13 | "github.com/cubicdaiya/nginx-build/util" 14 | ) 15 | 16 | const DefaultDownloadTimeout = time.Duration(900) * time.Second 17 | 18 | func extractArchive(path string) error { 19 | return command.Run([]string{"tar", "zxvf", path}) 20 | } 21 | 22 | func download(b *builder.Builder) error { 23 | c := &http.Client{ 24 | Timeout: DefaultDownloadTimeout, 25 | } 26 | res, err := c.Get(b.DownloadURL()) 27 | if err != nil { 28 | return err 29 | } 30 | defer res.Body.Close() 31 | 32 | if res.StatusCode != http.StatusOK { 33 | return fmt.Errorf("failed to download %s. %s", b.DownloadURL(), res.Status) 34 | } 35 | 36 | tmpFileName := b.ArchivePath() + ".download" 37 | f, err := os.Create(tmpFileName) 38 | if err != nil { 39 | return err 40 | } 41 | defer f.Close() 42 | 43 | if _, err := io.Copy(f, res.Body); err != nil && err != io.EOF { 44 | return err 45 | } 46 | 47 | if err := os.Rename(tmpFileName, b.ArchivePath()); err != nil { 48 | return err 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func downloadAndExtract(b *builder.Builder) error { 55 | if !util.FileExists(b.SourcePath()) { 56 | if !util.FileExists(b.ArchivePath()) { 57 | 58 | log.Printf("Download %s.....", b.SourcePath()) 59 | 60 | if err := download(b); err != nil { 61 | return fmt.Errorf("Failed to download %s. %s", b.SourcePath(), err.Error()) 62 | } 63 | } 64 | 65 | log.Printf("Extract %s.....", b.ArchivePath()) 66 | 67 | if err := extractArchive(b.ArchivePath()); err != nil { 68 | return fmt.Errorf("Failed to extract %s. %s", b.ArchivePath(), err.Error()) 69 | } 70 | } else { 71 | log.Printf("%s already exists.", b.SourcePath()) 72 | } 73 | return nil 74 | } 75 | 76 | func downloadAndExtractParallel(b *builder.Builder) { 77 | if err := downloadAndExtract(b); err != nil { 78 | util.PrintFatalMsg(err, b.LogPath()) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cubicdaiya/nginx-build 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubicdaiya/nginx-build/6ea9d34d400605d5ebd8f4fa5a4ecb5893297999/go.sum -------------------------------------------------------------------------------- /images/nginx-build.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubicdaiya/nginx-build/6ea9d34d400605d5ebd8f4fa5a4ecb5893297999/images/nginx-build.gif -------------------------------------------------------------------------------- /message.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/exec" 9 | "runtime" 10 | "runtime/debug" 11 | ) 12 | 13 | var ( 14 | NginxBuildVersion string 15 | ) 16 | 17 | func nginxBuildVersion() string { 18 | if NginxBuildVersion != "" { 19 | return NginxBuildVersion 20 | } 21 | info, ok := debug.ReadBuildInfo() 22 | if !ok { 23 | return "(devel)" 24 | } 25 | return info.Main.Version 26 | } 27 | 28 | func printNginxBuildVersion() { 29 | fmt.Printf(`nginx-build %s 30 | Compiler: %s %s 31 | Copyright (C) 2014- Tatsuhiko Kubo 32 | `, 33 | nginxBuildVersion(), 34 | runtime.Compiler, 35 | runtime.Version()) 36 | 37 | } 38 | 39 | func printConfigureOptions() error { 40 | cmd := exec.Command("objs/nginx", "-V") 41 | cmd.Stdout = os.Stdout 42 | cmd.Stderr = os.Stderr 43 | return cmd.Run() 44 | } 45 | 46 | func printFirstMsg() { 47 | fmt.Printf(`nginx-build: %s 48 | Compiler: %s %s 49 | `, 50 | nginxBuildVersion(), 51 | runtime.Compiler, 52 | runtime.Version()) 53 | } 54 | 55 | func printLastMsg(workDir, srcDir string, openResty, configureOnly bool) { 56 | log.Println("Complete building nginx!") 57 | 58 | if !openResty { 59 | if !configureOnly { 60 | fmt.Println() 61 | err := printConfigureOptions() 62 | if err != nil { 63 | fmt.Println(err.Error()) 64 | } 65 | } 66 | } 67 | fmt.Println() 68 | 69 | lastMsgFormat := `Enter the following command for install nginx. 70 | 71 | $ cd %s/%s%s 72 | $ sudo make install 73 | ` 74 | if configureOnly { 75 | log.Printf(lastMsgFormat, workDir, srcDir, "\n $ make") 76 | } else { 77 | log.Printf(lastMsgFormat, workDir, srcDir, "") 78 | } 79 | } 80 | 81 | func usage() { 82 | fmt.Fprintf(os.Stdout, "Usage of %s:\n", os.Args[0]) 83 | flag.VisitAll(func(f *flag.Flag) { 84 | if !isNginxBuildOption(f.Name) { 85 | return 86 | } 87 | s := fmt.Sprintf(" -%s", f.Name) 88 | s += "\n\t" 89 | s += f.Usage 90 | defValue := defaultStringValue(f.Name) 91 | if defValue != "" { 92 | s += fmt.Sprintf(" ( default: %s )", defValue) 93 | } 94 | 95 | fmt.Fprintf(os.Stdout, "%s\n", s) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /module3rd/download.go: -------------------------------------------------------------------------------- 1 | package module3rd 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/cubicdaiya/nginx-build/command" 10 | "github.com/cubicdaiya/nginx-build/util" 11 | ) 12 | 13 | func DownloadAndExtractParallel(m Module3rd) { 14 | if util.FileExists(m.Name) { 15 | log.Printf("%s already exists.", m.Name) 16 | return 17 | } 18 | 19 | if m.Form != "local" { 20 | if len(m.Rev) > 0 { 21 | log.Printf("Download %s-%s.....", m.Name, m.Rev) 22 | } else { 23 | log.Printf("Download %s.....", m.Name) 24 | } 25 | 26 | logName := fmt.Sprintf("%s.log", m.Name) 27 | 28 | err := download(m, logName) 29 | if err != nil { 30 | util.PrintFatalMsg(err, logName) 31 | } 32 | } else if !util.FileExists(m.Url) { 33 | log.Fatalf("no such directory:%s", m.Url) 34 | } 35 | } 36 | 37 | func download(m Module3rd, logName string) error { 38 | form := m.Form 39 | url := m.Url 40 | 41 | switch form { 42 | case "git": 43 | args := []string{form, "clone", "--recursive", url} 44 | if command.VerboseEnabled { 45 | return command.Run(args) 46 | } 47 | 48 | f, err := os.Create(logName) 49 | if err != nil { 50 | return command.Run(args) 51 | } 52 | defer f.Close() 53 | 54 | cmd, err := command.Make(args) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | writer := bufio.NewWriter(f) 60 | defer writer.Flush() 61 | 62 | cmd.Stderr = writer 63 | 64 | return cmd.Run() 65 | case "hg": 66 | args := []string{form, "clone", url} 67 | if command.VerboseEnabled { 68 | return command.Run(args) 69 | } 70 | 71 | f, err := os.Create(logName) 72 | if err != nil { 73 | return command.Run(args) 74 | } 75 | defer f.Close() 76 | 77 | cmd, err := command.Make(args) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | writer := bufio.NewWriter(f) 83 | defer writer.Flush() 84 | 85 | cmd.Stderr = writer 86 | 87 | return cmd.Run() 88 | case "local": // not implemented yet 89 | return nil 90 | } 91 | 92 | return fmt.Errorf("form=%s is not supported", form) 93 | } 94 | -------------------------------------------------------------------------------- /module3rd/load.go: -------------------------------------------------------------------------------- 1 | package module3rd 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func Load(path string) ([]Module3rd, error) { 10 | var modules []Module3rd 11 | if len(path) > 0 { 12 | f, err := os.Open(path) 13 | if err != nil { 14 | return modules, err 15 | } 16 | if err := json.NewDecoder(f).Decode(&modules); err != nil { 17 | return modules, fmt.Errorf("modulesConfPath(%s) is invalid JSON.", path) 18 | } 19 | for i, _ := range modules { 20 | if modules[i].Form == "" { 21 | modules[i].Form = "git" 22 | } 23 | } 24 | } 25 | return modules, nil 26 | } 27 | -------------------------------------------------------------------------------- /module3rd/load_test.go: -------------------------------------------------------------------------------- 1 | package module3rd 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestModules3rd(t *testing.T) { 8 | 9 | modules3rdConf := "../config/modules.json.example" 10 | modules3rd, err := Load(modules3rdConf) 11 | if err != nil { 12 | t.Fatalf("Failed to load %s", modules3rdConf) 13 | } 14 | 15 | for _, m := range modules3rd { 16 | var want Module3rd 17 | switch m.Name { 18 | case "ngx_http_hello_world": 19 | want.Name = "ngx_http_hello_world" 20 | want.Form = "git" 21 | want.Url = "https://github.com/cubicdaiya/ngx_http_hello_world" 22 | want.Dynamic = false 23 | default: 24 | t.Fatalf("unexpected module: %v", m) 25 | } 26 | 27 | if m != want { 28 | t.Fatalf("got: %v, want: %v", m, want) 29 | } 30 | } 31 | } 32 | 33 | func TestModules3rdWithNJS(t *testing.T) { 34 | 35 | modules3rdConf := "../config/modules.json.njs" 36 | modules3rd, err := Load(modules3rdConf) 37 | if err != nil { 38 | t.Fatalf("Failed to load %s", modules3rdConf) 39 | } 40 | 41 | for _, m := range modules3rd { 42 | var want Module3rd 43 | switch m.Name { 44 | case "njs/nginx": 45 | want.Name = "njs/nginx" 46 | want.Form = "hg" 47 | want.Url = "https://hg.nginx.org/njs" 48 | want.Dynamic = false 49 | want.Shprov = "./configure && make" 50 | want.ShprovDir = ".." 51 | default: 52 | t.Fatalf("unexpected module: %v", m) 53 | } 54 | 55 | if m != want { 56 | t.Fatalf("got: %v, want: %v", m, want) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /module3rd/module3rd.go: -------------------------------------------------------------------------------- 1 | package module3rd 2 | 3 | type Module3rd struct { 4 | Name string `json:"name"` 5 | Form string `json:"form"` 6 | Url string `json:"url"` 7 | Rev string `json:"rev"` 8 | Dynamic bool `json:"dynamic"` 9 | Shprov string `json:"shprov"` 10 | ShprovDir string `json:"shprovdir"` 11 | } 12 | -------------------------------------------------------------------------------- /module3rd/provide.go: -------------------------------------------------------------------------------- 1 | package module3rd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | 9 | "github.com/cubicdaiya/nginx-build/command" 10 | "github.com/cubicdaiya/nginx-build/util" 11 | ) 12 | 13 | func Provide(m *Module3rd) error { 14 | if len(m.Rev) > 0 { 15 | dir := util.SaveCurrentDir() 16 | os.Chdir(m.Name) 17 | if err := switchRev(m.Form, m.Rev); err != nil { 18 | return fmt.Errorf("%s (%s checkout %s): %s", m.Name, m.Form, m.Rev, err.Error()) 19 | } 20 | os.Chdir(dir) 21 | } 22 | 23 | if len(m.Shprov) > 0 { 24 | dir := util.SaveCurrentDir() 25 | if len(m.ShprovDir) > 0 { 26 | os.Chdir(m.Name + "/" + m.ShprovDir) 27 | } else { 28 | os.Chdir(m.Name) 29 | } 30 | if err := provideShell(m.Shprov); err != nil { 31 | return fmt.Errorf("%s's shprov(%s): %s", m.Name, m.Shprov, err.Error()) 32 | } 33 | os.Chdir(dir) 34 | } 35 | 36 | return nil 37 | } 38 | 39 | func provideShell(sh string) error { 40 | if strings.TrimSpace(sh) == "" { 41 | return nil 42 | } 43 | if command.VerboseEnabled { 44 | return command.Run([]string{"sh", "-c", sh}) 45 | } 46 | 47 | cmd := exec.Command("sh", "-c", sh) 48 | return cmd.Run() 49 | } 50 | 51 | func switchRev(form, rev string) error { 52 | var err error 53 | 54 | switch form { 55 | case "git": 56 | err = command.Run([]string{"git", "checkout", rev}) 57 | case "hg": 58 | err = command.Run([]string{"hg", "checkout", rev}) 59 | default: 60 | err = fmt.Errorf("form=%s is not supported", form) 61 | } 62 | 63 | return err 64 | } 65 | -------------------------------------------------------------------------------- /multistringflag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // StringFlag implements the methods for flag.Value. 8 | // 9 | // nginx-build allows multiple flags in the specified options 10 | // such as `--add-module` and `--add-dynamic-module`. 11 | type StringFlag []string 12 | 13 | func (s *StringFlag) String() string { 14 | return strings.Join(*s, ",") 15 | } 16 | 17 | func (s *StringFlag) Set(v string) error { 18 | *s = append(*s, v) 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /nginx-build.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "runtime" 9 | "strings" 10 | "sync" 11 | "syscall" 12 | 13 | "github.com/cubicdaiya/nginx-build/builder" 14 | "github.com/cubicdaiya/nginx-build/command" 15 | "github.com/cubicdaiya/nginx-build/configure" 16 | "github.com/cubicdaiya/nginx-build/module3rd" 17 | "github.com/cubicdaiya/nginx-build/util" 18 | ) 19 | 20 | var ( 21 | nginxBuildOptions Options 22 | ) 23 | 24 | func init() { 25 | nginxBuildOptions = makeNginxBuildOptions() 26 | } 27 | 28 | // fake flag for --with-xxx=dynamic 29 | func overrideUnableParseFlags() { 30 | for i, arg := range os.Args { 31 | if strings.Contains(arg, "with-http_xslt_module=dynamic") { 32 | os.Args[i] = "--with-http_xslt_module_dynamic" 33 | } 34 | if strings.Contains(arg, "with-http_image_filter_module=dynamic") { 35 | os.Args[i] = "--with-http_image_filter_module_dynamic" 36 | } 37 | if strings.Contains(arg, "with-http_geoip_module=dynamic") { 38 | os.Args[i] = "--with-http_geoip_module_dynamic" 39 | } 40 | if strings.Contains(arg, "with-http_perl_module=dynamic") { 41 | os.Args[i] = "--with-http_perl_module_dynamic" 42 | } 43 | if strings.Contains(arg, "with-mail=dynamic") { 44 | os.Args[i] = "--with-mail_dynamic" 45 | } 46 | if strings.Contains(arg, "with-stream=dynamic") { 47 | os.Args[i] = "--with-stream_dynamic" 48 | } 49 | if strings.Contains(arg, "with-stream_geoip_module=dynamic") { 50 | os.Args[i] = "--with-stream_geoip_module_dynamic" 51 | } 52 | } 53 | } 54 | 55 | func main() { 56 | var ( 57 | multiflagPatch StringFlag 58 | ) 59 | 60 | // Parse flags 61 | for k, v := range nginxBuildOptions.Bools { 62 | v.Enabled = flag.Bool(k, false, v.Desc) 63 | nginxBuildOptions.Bools[k] = v 64 | } 65 | for k, v := range nginxBuildOptions.Values { 66 | if k == "patch" { 67 | flag.Var(&multiflagPatch, k, v.Desc) 68 | } else { 69 | v.Value = flag.String(k, v.Default, v.Desc) 70 | nginxBuildOptions.Values[k] = v 71 | } 72 | } 73 | for k, v := range nginxBuildOptions.Numbers { 74 | v.Value = flag.Int(k, v.Default, v.Desc) 75 | nginxBuildOptions.Numbers[k] = v 76 | } 77 | 78 | overrideUnableParseFlags() 79 | 80 | var ( 81 | configureOptions configure.Options 82 | multiflag StringFlag 83 | multiflagDynamic StringFlag 84 | ) 85 | 86 | argsString := configure.MakeArgsString() 87 | for k, v := range argsString { 88 | if k == "add-module" { 89 | flag.Var(&multiflag, k, v.Desc) 90 | } else if k == "add-dynamic-module" { 91 | flag.Var(&multiflagDynamic, k, v.Desc) 92 | } else { 93 | v.Value = flag.String(k, "", v.Desc) 94 | argsString[k] = v 95 | } 96 | } 97 | 98 | argsBool := configure.MakeArgsBool() 99 | for k, v := range argsBool { 100 | v.Enabled = flag.Bool(k, false, v.Desc) 101 | argsBool[k] = v 102 | } 103 | 104 | flag.CommandLine.SetOutput(os.Stdout) 105 | // The output of original flag.Usage() is too long 106 | defaultUsage := flag.Usage 107 | flag.Usage = usage 108 | flag.Parse() 109 | 110 | jobs := nginxBuildOptions.Numbers["j"].Value 111 | 112 | verbose := nginxBuildOptions.Bools["verbose"].Enabled 113 | pcreStatic := nginxBuildOptions.Bools["pcre"].Enabled 114 | openSSLStatic := nginxBuildOptions.Bools["openssl"].Enabled 115 | libreSSLStatic := nginxBuildOptions.Bools["libressl"].Enabled 116 | zlibStatic := nginxBuildOptions.Bools["zlib"].Enabled 117 | clear := nginxBuildOptions.Bools["clear"].Enabled 118 | versionPrint := nginxBuildOptions.Bools["version"].Enabled 119 | versionsPrint := nginxBuildOptions.Bools["versions"].Enabled 120 | openResty := nginxBuildOptions.Bools["openresty"].Enabled 121 | freenginx := nginxBuildOptions.Bools["freenginx"].Enabled 122 | configureOnly := nginxBuildOptions.Bools["configureonly"].Enabled 123 | idempotent := nginxBuildOptions.Bools["idempotent"].Enabled 124 | helpAll := nginxBuildOptions.Bools["help-all"].Enabled 125 | 126 | version := nginxBuildOptions.Values["v"].Value 127 | nginxConfigurePath := nginxBuildOptions.Values["c"].Value 128 | modulesConfPath := nginxBuildOptions.Values["m"].Value 129 | workParentDir := nginxBuildOptions.Values["d"].Value 130 | pcreVersion := nginxBuildOptions.Values["pcreversion"].Value 131 | openSSLVersion := nginxBuildOptions.Values["opensslversion"].Value 132 | libreSSLVersion := nginxBuildOptions.Values["libresslversion"].Value 133 | zlibVersion := nginxBuildOptions.Values["zlibversion"].Value 134 | openRestyVersion := nginxBuildOptions.Values["openrestyversion"].Value 135 | freenginxVersion := nginxBuildOptions.Values["freenginxversion"].Value 136 | patchOption := nginxBuildOptions.Values["patch-opt"].Value 137 | 138 | // Allow multiple flags for `--patch` 139 | { 140 | tmp := nginxBuildOptions.Values["patch"] 141 | tmp2 := multiflagPatch.String() 142 | tmp.Value = &tmp2 143 | nginxBuildOptions.Values["patch"] = tmp 144 | } 145 | 146 | // Allow multiple flags for `--add-module` 147 | { 148 | tmp := argsString["add-module"] 149 | tmp2 := multiflag.String() 150 | tmp.Value = &tmp2 151 | argsString["add-module"] = tmp 152 | } 153 | 154 | // Allow multiple flags for `--add-dynamic-module` 155 | { 156 | tmp := argsString["add-dynamic-module"] 157 | tmp2 := multiflagDynamic.String() 158 | tmp.Value = &tmp2 159 | argsString["add-dynamic-module"] = tmp 160 | } 161 | 162 | patchPath := nginxBuildOptions.Values["patch"].Value 163 | configureOptions.Values = argsString 164 | configureOptions.Bools = argsBool 165 | 166 | if *helpAll { 167 | defaultUsage() 168 | return 169 | } 170 | 171 | if *versionPrint { 172 | printNginxBuildVersion() 173 | return 174 | } 175 | 176 | if *versionsPrint { 177 | printNginxVersions() 178 | return 179 | } 180 | 181 | printFirstMsg() 182 | 183 | // set verbose mode 184 | command.VerboseEnabled = *verbose 185 | 186 | var nginxBuilder builder.Builder 187 | if *openResty && *freenginx { 188 | log.Fatal("select one between '-openresty' and '-freenginx'.") 189 | } 190 | if *openSSLStatic && *libreSSLStatic { 191 | log.Fatal("select one between '-openssl' and '-libressl'.") 192 | } 193 | if *openResty { 194 | nginxBuilder = builder.MakeBuilder(builder.ComponentOpenResty, *openRestyVersion) 195 | } else if *freenginx { 196 | nginxBuilder = builder.MakeBuilder(builder.ComponentFreenginx, *freenginxVersion) 197 | } else { 198 | nginxBuilder = builder.MakeBuilder(builder.ComponentNginx, *version) 199 | } 200 | pcreBuilder := builder.MakeLibraryBuilder(builder.ComponentPcre, *pcreVersion, *pcreStatic) 201 | openSSLBuilder := builder.MakeLibraryBuilder(builder.ComponentOpenSSL, *openSSLVersion, *openSSLStatic) 202 | libreSSLBuilder := builder.MakeLibraryBuilder(builder.ComponentLibreSSL, *libreSSLVersion, *libreSSLStatic) 203 | zlibBuilder := builder.MakeLibraryBuilder(builder.ComponentZlib, *zlibVersion, *zlibStatic) 204 | 205 | if *idempotent { 206 | builders := []builder.Builder{ 207 | nginxBuilder, 208 | pcreBuilder, 209 | openSSLBuilder, 210 | libreSSLBuilder, 211 | zlibBuilder, 212 | } 213 | 214 | isSame, err := builder.IsSameVersion(builders) 215 | if err != nil { 216 | log.Println("[notice]", err) 217 | } 218 | if isSame { 219 | log.Println("Installed nginx is same.") 220 | return 221 | } 222 | } 223 | 224 | // change default umask 225 | _ = syscall.Umask(0) 226 | 227 | versionCheck(*version) 228 | 229 | nginxConfigure, err := util.FileGetContents(*nginxConfigurePath) 230 | if err != nil { 231 | log.Fatal(err) 232 | } 233 | nginxConfigure = configure.Normalize(nginxConfigure) 234 | 235 | modules3rd, err := module3rd.Load(*modulesConfPath) 236 | if err != nil { 237 | log.Fatal(err) 238 | } 239 | 240 | if len(*workParentDir) == 0 { 241 | log.Fatal("set working directory with -d") 242 | } 243 | 244 | if !util.FileExists(*workParentDir) { 245 | err := os.Mkdir(*workParentDir, 0755) 246 | if err != nil { 247 | log.Fatalf("Failed to create working directory(%s) does not exist.", *workParentDir) 248 | } 249 | } 250 | 251 | var workDir string 252 | if *openResty { 253 | workDir = *workParentDir + "/openresty/" + *openRestyVersion 254 | } else if *freenginx { 255 | workDir = *workParentDir + "/freenginx/" + *freenginxVersion 256 | } else { 257 | workDir = *workParentDir + "/nginx/" + *version 258 | } 259 | 260 | if *clear { 261 | err := util.ClearWorkDir(workDir) 262 | if err != nil { 263 | log.Fatal(err) 264 | } 265 | } 266 | 267 | if !util.FileExists(workDir) { 268 | err := os.MkdirAll(workDir, 0755) 269 | if err != nil { 270 | log.Fatalf("Failed to create working directory(%s) does not exist.", workDir) 271 | } 272 | } 273 | 274 | rootDir := util.SaveCurrentDir() 275 | err = os.Chdir(workDir) 276 | if err != nil { 277 | log.Fatal(err) 278 | } 279 | 280 | // remove nginx source code applyed patch 281 | if *patchPath != "" && util.FileExists(nginxBuilder.SourcePath()) { 282 | err := os.RemoveAll(nginxBuilder.SourcePath()) 283 | if err != nil { 284 | log.Fatal(err) 285 | } 286 | } 287 | 288 | var wg sync.WaitGroup 289 | if *pcreStatic { 290 | wg.Add(1) 291 | go func() { 292 | downloadAndExtractParallel(&pcreBuilder) 293 | wg.Done() 294 | }() 295 | } 296 | 297 | if *openSSLStatic { 298 | wg.Add(1) 299 | go func() { 300 | downloadAndExtractParallel(&openSSLBuilder) 301 | wg.Done() 302 | }() 303 | } 304 | 305 | if *libreSSLStatic { 306 | wg.Add(1) 307 | go func() { 308 | downloadAndExtractParallel(&libreSSLBuilder) 309 | wg.Done() 310 | }() 311 | } 312 | 313 | if *zlibStatic { 314 | wg.Add(1) 315 | go func() { 316 | downloadAndExtractParallel(&zlibBuilder) 317 | wg.Done() 318 | }() 319 | } 320 | 321 | wg.Add(1) 322 | go func() { 323 | downloadAndExtractParallel(&nginxBuilder) 324 | wg.Done() 325 | }() 326 | 327 | if len(modules3rd) > 0 { 328 | wg.Add(len(modules3rd)) 329 | for _, m := range modules3rd { 330 | go func(m module3rd.Module3rd) { 331 | module3rd.DownloadAndExtractParallel(m) 332 | wg.Done() 333 | }(m) 334 | } 335 | 336 | } 337 | 338 | // wait until all downloading processes by goroutine finish 339 | wg.Wait() 340 | 341 | if len(modules3rd) > 0 { 342 | for _, m := range modules3rd { 343 | if err := module3rd.Provide(&m); err != nil { 344 | log.Fatal(err) 345 | } 346 | } 347 | } 348 | 349 | // cd workDir/nginx-${version} 350 | os.Chdir(nginxBuilder.SourcePath()) 351 | 352 | var dependencies []builder.StaticLibrary 353 | if *pcreStatic { 354 | dependencies = append(dependencies, builder.MakeStaticLibrary(&pcreBuilder)) 355 | } 356 | 357 | if *openSSLStatic { 358 | dependencies = append(dependencies, builder.MakeStaticLibrary(&openSSLBuilder)) 359 | } 360 | 361 | if *libreSSLStatic { 362 | dependencies = append(dependencies, builder.MakeStaticLibrary(&libreSSLBuilder)) 363 | } 364 | 365 | if *zlibStatic { 366 | dependencies = append(dependencies, builder.MakeStaticLibrary(&zlibBuilder)) 367 | } 368 | 369 | log.Printf("Generate configure script for %s.....", nginxBuilder.SourcePath()) 370 | 371 | if *pcreStatic && pcreBuilder.IsIncludeWithOption(nginxConfigure) { 372 | log.Println(pcreBuilder.WarnMsgWithLibrary()) 373 | } 374 | 375 | if *openSSLStatic && openSSLBuilder.IsIncludeWithOption(nginxConfigure) { 376 | log.Println(openSSLBuilder.WarnMsgWithLibrary()) 377 | } 378 | 379 | if *libreSSLStatic && libreSSLBuilder.IsIncludeWithOption(nginxConfigure) { 380 | log.Println(libreSSLBuilder.WarnMsgWithLibrary()) 381 | } 382 | 383 | if *zlibStatic && zlibBuilder.IsIncludeWithOption(nginxConfigure) { 384 | log.Println(zlibBuilder.WarnMsgWithLibrary()) 385 | } 386 | 387 | configureScript := configure.Generate(nginxConfigure, modules3rd, dependencies, configureOptions, rootDir, *openResty, *jobs) 388 | 389 | err = os.WriteFile("./nginx-configure", []byte(configureScript), 0655) 390 | if err != nil { 391 | log.Fatalf("Failed to generate configure script for %s", nginxBuilder.SourcePath()) 392 | } 393 | 394 | util.Patch(*patchPath, *patchOption, rootDir, false) 395 | 396 | // reverts source code with patch -R when the build was interrupted. 397 | if *patchPath != "" { 398 | sigChannel := make(chan os.Signal, 1) 399 | signal.Notify(sigChannel, os.Interrupt) 400 | go func() { 401 | <-sigChannel 402 | util.Patch(*patchPath, *patchOption, rootDir, true) 403 | }() 404 | } 405 | 406 | log.Printf("Configure %s.....", nginxBuilder.SourcePath()) 407 | 408 | err = configure.Run() 409 | if err != nil { 410 | log.Printf("Failed to configure %s\n", nginxBuilder.SourcePath()) 411 | util.Patch(*patchPath, *patchOption, rootDir, true) 412 | util.PrintFatalMsg(err, "nginx-configure.log") 413 | } 414 | 415 | if *configureOnly { 416 | util.Patch(*patchPath, *patchOption, rootDir, true) 417 | printLastMsg(workDir, nginxBuilder.SourcePath(), *openResty, *configureOnly) 418 | return 419 | } 420 | 421 | log.Printf("Build %s.....", nginxBuilder.SourcePath()) 422 | 423 | if *openSSLStatic { 424 | // Sometimes machine hardware name('uname -m') is different 425 | // from machine processor architecture name('uname -p') on Mac. 426 | // Specifically, `uname -p` is 'i386' and `uname -m` is 'x86_64'. 427 | // In this case, a build of OpenSSL fails. 428 | // So it needs to convince OpenSSL with KERNEL_BITS. 429 | if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" { 430 | os.Setenv("KERNEL_BITS", "64") 431 | } 432 | } 433 | 434 | err = builder.BuildNginx(*jobs) 435 | if err != nil { 436 | log.Printf("Failed to build %s\n", nginxBuilder.SourcePath()) 437 | util.Patch(*patchPath, *patchOption, rootDir, true) 438 | util.PrintFatalMsg(err, "nginx-build.log") 439 | } 440 | 441 | printLastMsg(workDir, nginxBuilder.SourcePath(), *openResty, *configureOnly) 442 | } 443 | -------------------------------------------------------------------------------- /openresty/openresty.go: -------------------------------------------------------------------------------- 1 | package openresty 2 | 3 | import ( 4 | "math" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | versionPattern *regexp.Regexp 12 | ) 13 | 14 | func init() { 15 | versionPattern = regexp.MustCompile(`([0-9]+)(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?`) 16 | } 17 | 18 | func Name(version string) string { 19 | version = versionPattern.FindString(version) 20 | 21 | numbers := strings.Split(version, ".") 22 | size := len(numbers) 23 | sum := 0 24 | for i := 0; i < size; i++ { 25 | n, err := strconv.Atoi(numbers[i]) 26 | if err != nil { 27 | return "ngx_openresty" 28 | } 29 | sum += int(math.Pow10(size-i-1)) * n 30 | } 31 | 32 | // the source distribution name of openresty is renamed in the 1.9.7.3 or later 33 | if sum > 1972 { 34 | return "openresty" 35 | } 36 | 37 | return "ngx_openresty" 38 | } 39 | -------------------------------------------------------------------------------- /openresty/openresty_test.go: -------------------------------------------------------------------------------- 1 | package openresty 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestOpenrestyName(t *testing.T) { 8 | 9 | tests := []struct { 10 | version string 11 | name string 12 | }{ 13 | { 14 | version: "1.9.7.2", 15 | name: "ngx_openresty", 16 | }, 17 | { 18 | version: "1.9.7.3", 19 | name: "openresty", 20 | }, 21 | { 22 | version: "1.9.7.4", 23 | name: "openresty", 24 | }, 25 | { 26 | version: "1.15.8.1rc1", 27 | name: "openresty", 28 | }, 29 | } 30 | 31 | for _, test := range tests { 32 | name := Name(test.version) 33 | if name != test.name { 34 | t.Fatalf("got: %v, want: %v", name, test.name) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /option.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | "strconv" 6 | 7 | "github.com/cubicdaiya/nginx-build/builder" 8 | ) 9 | 10 | type Options struct { 11 | Values map[string]OptionValue 12 | Bools map[string]OptionBool 13 | Numbers map[string]OptionNumber 14 | } 15 | 16 | type OptionValue struct { 17 | Desc string 18 | Value *string 19 | Default string 20 | } 21 | 22 | type OptionBool struct { 23 | Desc string 24 | Enabled *bool 25 | // all options are false by default 26 | //Default bool 27 | } 28 | 29 | type OptionNumber struct { 30 | Desc string 31 | Value *int 32 | Default int 33 | } 34 | 35 | func makeNginxBuildOptions() Options { 36 | var nginxBuildOptions Options 37 | argsNumber := make(map[string]OptionNumber) 38 | argsBool := make(map[string]OptionBool) 39 | argsString := make(map[string]OptionValue) 40 | 41 | argsNumber["j"] = OptionNumber{ 42 | Desc: "jobs to build nginx", 43 | Default: runtime.NumCPU(), 44 | } 45 | 46 | argsBool["verbose"] = OptionBool{ 47 | Desc: "verbose mode", 48 | } 49 | argsBool["pcre"] = OptionBool{ 50 | Desc: "embedded PCRE staticlibrary", 51 | } 52 | argsBool["openssl"] = OptionBool{ 53 | Desc: "embedded OpenSSL staticlibrary", 54 | } 55 | argsBool["libressl"] = OptionBool{ 56 | Desc: "embedded LibreSSL staticlibrary", 57 | } 58 | argsBool["zlib"] = OptionBool{ 59 | Desc: "embedded zlib staticlibrary", 60 | } 61 | argsBool["clear"] = OptionBool{ 62 | Desc: "remove entries in working directory", 63 | } 64 | argsBool["version"] = OptionBool{ 65 | Desc: "print nginx-build version", 66 | } 67 | argsBool["versions"] = OptionBool{ 68 | Desc: "print nginx versions", 69 | } 70 | argsBool["openresty"] = OptionBool{ 71 | Desc: "download openresty instead of nginx", 72 | } 73 | argsBool["freenginx"] = OptionBool{ 74 | Desc: "download freenginx instead of nginx", 75 | } 76 | argsBool["configureonly"] = OptionBool{ 77 | Desc: "configure nginx only not building", 78 | } 79 | argsBool["idempotent"] = OptionBool{ 80 | Desc: "build nginx if already installed nginx version is different", 81 | } 82 | argsBool["help-all"] = OptionBool{ 83 | Desc: "print all flags", 84 | } 85 | 86 | argsString["v"] = OptionValue{ 87 | Desc: "nginx version", 88 | Default: builder.NginxVersion, 89 | } 90 | argsString["c"] = OptionValue{ 91 | Desc: "configuration file for building nginx", 92 | Default: "", 93 | } 94 | argsString["m"] = OptionValue{ 95 | Desc: "configuration file for 3rd party modules", 96 | Default: "", 97 | } 98 | argsString["d"] = OptionValue{ 99 | Desc: "working directory", 100 | Default: "", 101 | } 102 | argsString["pcreversion"] = OptionValue{ 103 | Desc: "PCRE version", 104 | Default: builder.PcreVersion, 105 | } 106 | argsString["opensslversion"] = OptionValue{ 107 | Desc: "OpenSSL version", 108 | Default: builder.OpenSSLVersion, 109 | } 110 | argsString["libresslversion"] = OptionValue{ 111 | Desc: "LibreSSL version", 112 | Default: builder.LibreSSLVersion, 113 | } 114 | argsString["zlibversion"] = OptionValue{ 115 | Desc: "zlib version", 116 | Default: builder.ZlibVersion, 117 | } 118 | argsString["openrestyversion"] = OptionValue{ 119 | Desc: "openresty version", 120 | Default: builder.OpenRestyVersion, 121 | } 122 | argsString["freenginxversion"] = OptionValue{ 123 | Desc: "freenginx version", 124 | Default: builder.FreenginxVersion, 125 | } 126 | argsString["patch"] = OptionValue{ 127 | Desc: "patch path for applying to nginx", 128 | Default: "", 129 | } 130 | argsString["patch-opt"] = OptionValue{ 131 | Desc: "option for patch", 132 | Default: "", 133 | } 134 | 135 | nginxBuildOptions.Bools = argsBool 136 | nginxBuildOptions.Values = argsString 137 | nginxBuildOptions.Numbers = argsNumber 138 | 139 | return nginxBuildOptions 140 | } 141 | 142 | func isNginxBuildOption(k string) bool { 143 | if _, ok := nginxBuildOptions.Bools[k]; ok { 144 | return true 145 | } 146 | if _, ok := nginxBuildOptions.Values[k]; ok { 147 | return true 148 | } 149 | if _, ok := nginxBuildOptions.Numbers[k]; ok { 150 | return true 151 | } 152 | return false 153 | } 154 | 155 | func defaultStringValue(k string) string { 156 | if _, ok := nginxBuildOptions.Values[k]; ok { 157 | return nginxBuildOptions.Values[k].Default 158 | } 159 | if _, ok := nginxBuildOptions.Numbers[k]; ok { 160 | return strconv.Itoa(nginxBuildOptions.Numbers[k].Default) 161 | } 162 | return "" 163 | } 164 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "customManagers": [ 7 | { 8 | "customType": "regex", 9 | "managerFilePatterns": [ 10 | "/.github/workflows/security.yml/" 11 | ], 12 | "datasourceTemplate": "golang-version", 13 | "depNameTemplate": "golang", 14 | "matchStrings": [ 15 | "go-version-input: (?[0-9]*.[0-9]*.[0-9]*)" 16 | ] 17 | }, 18 | { 19 | "customType": "regex", 20 | "managerFilePatterns": [ 21 | "/.github/workflows/release.yml/" 22 | ], 23 | "datasourceTemplate": "golang-version", 24 | "depNameTemplate": "golang", 25 | "matchStrings": [ 26 | "go-version: (?[0-9]*.[0-9]*.[0-9]*)" 27 | ] 28 | }, 29 | { 30 | "customType": "regex", 31 | "managerFilePatterns": [ 32 | "/builder/const.go/" 33 | ], 34 | "extractVersionTemplate": "^v(?.*)$", 35 | "matchStrings": [ 36 | "datasource=(?.*?) depName=(?.*?)( versioning=(?.*?))?\n.* \"(?[0-9.]*)\"" 37 | ], 38 | "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}" 39 | }, 40 | { 41 | "customType": "regex", 42 | "managerFilePatterns": [ 43 | "/builder/const.go/" 44 | ], 45 | "extractVersionTemplate": "^release-(?[0-9]*.[0-9]*[02468].[0-9]*)$", 46 | "datasourceTemplate": "github-tags", 47 | "depNameTemplate": "nginx/nginx", 48 | "matchStrings": [ 49 | "NginxVersion\\s+=\\s\"(?[0-9.]*)\"" 50 | ] 51 | }, 52 | { 53 | "customType": "regex", 54 | "managerFilePatterns": [ 55 | "/builder/const.go/" 56 | ], 57 | "extractVersionTemplate": "^release-(?[0-9]*.[0-9]*[02468].[0-9]*)$", 58 | "datasourceTemplate": "github-tags", 59 | "depNameTemplate": "freenginx/nginx", 60 | "matchStrings": [ 61 | "FreenginxVersion\\s+=\\s\"(?[0-9.]*)\"" 62 | ] 63 | }, 64 | { 65 | "customType": "regex", 66 | "managerFilePatterns": [ 67 | "/builder/const.go/" 68 | ], 69 | "extractVersionTemplate": "^v(?.*)$", 70 | "datasourceTemplate": "github-tags", 71 | "depNameTemplate": "madler/zlib", 72 | "matchStrings": [ 73 | "ZlibVersion\\s+=\\s\"(?[0-9.]*)\"" 74 | ] 75 | }, 76 | { 77 | "customType": "regex", 78 | "managerFilePatterns": [ 79 | "/builder/const.go/" 80 | ], 81 | "extractVersionTemplate": "^pcre2-(?[0-9]*\\.[0-9]*)$", 82 | "datasourceTemplate": "github-tags", 83 | "depNameTemplate": "PCRE2Project/pcre2", 84 | "matchStrings": [ 85 | "PcreVersion\\s+=\\s\"(?[0-9.]*)\"" 86 | ] 87 | }, 88 | { 89 | "customType": "regex", 90 | "managerFilePatterns": [ 91 | "/builder/const.go/" 92 | ], 93 | "extractVersionTemplate": "^v(?[0-9]*.[0-9]*.[0-9]*)$", 94 | "datasourceTemplate": "github-tags", 95 | "depNameTemplate": "libressl/portable", 96 | "matchStrings": [ 97 | "LibreSSLVersion\\s+=\\s\"(?[0-9.]*)\"" 98 | ] 99 | }, 100 | { 101 | "customType": "regex", 102 | "managerFilePatterns": [ 103 | "/builder/const.go/" 104 | ], 105 | "extractVersionTemplate": "^openssl-(?.*)$", 106 | "datasourceTemplate": "github-tags", 107 | "depNameTemplate": "openssl/openssl", 108 | "matchStrings": [ 109 | "OpenSSLVersion\\s+=\\s\"(?[0-9.]*)\"" 110 | ] 111 | }, 112 | { 113 | "customType": "regex", 114 | "managerFilePatterns": [ 115 | "/builder/const.go/" 116 | ], 117 | "extractVersionTemplate": "^v(?[0-9.]*)$", 118 | "datasourceTemplate": "github-tags", 119 | "depNameTemplate": "openresty/openresty", 120 | "matchStrings": [ 121 | "OpenRestyVersion\\s+=\\s\"(?[0-9.]*)\"" 122 | ] 123 | } 124 | ], 125 | "packageRules": [ 126 | { 127 | "matchDatasources": [ 128 | "github-tags" 129 | ], 130 | "matchPackageNames": [ 131 | "openresty/openresty" 132 | ], 133 | "versioning": "regex:^(?[0-9]+)\\.(?[0-9]+)\\.(?[0-9]+)(\\.(?[0-9]*))?$" 134 | } 135 | ], 136 | "postUpdateOptions": [ 137 | "gomodTidy", 138 | "gomodUpdateImportPaths" 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /util/file.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func FileExists(path string) bool { 10 | _, err := os.Stat(path) 11 | if err != nil { 12 | return false 13 | } 14 | return true 15 | } 16 | 17 | func IsDirectory(path string) (bool, error) { 18 | stat, err := os.Stat(path) 19 | if err != nil { 20 | return false, err 21 | } 22 | return stat.IsDir(), nil 23 | } 24 | 25 | func ListDirectory(path string) ([]string, error) { 26 | var fileNames []string 27 | 28 | err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { 29 | if !info.IsDir() { 30 | fileNames = append(fileNames, path) 31 | } 32 | return nil 33 | }) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return fileNames, nil 39 | } 40 | 41 | func SaveCurrentDir() string { 42 | prevDir, _ := filepath.Abs(".") 43 | return prevDir 44 | } 45 | 46 | func FileGetContents(path string) (string, error) { 47 | conf := "" 48 | if len(path) > 0 { 49 | confb, err := os.ReadFile(path) 50 | if err != nil { 51 | return "", fmt.Errorf("confPath(%s) does not exist.", path) 52 | } 53 | conf = string(confb) 54 | } 55 | return conf, nil 56 | } 57 | 58 | func ClearWorkDir(workDir string) error { 59 | err := os.RemoveAll(workDir) 60 | if err != nil { 61 | // workaround for the restriction of os.RemoveAll() 62 | // os.RemoveAll() calls fd.Readdirnames(100). 63 | // So os.RemoveAll() does not always remove all entries. 64 | // Some 3rd-party module (e.g. lua-nginx-module) tumbles this restriction. 65 | if FileExists(workDir) { 66 | err = os.RemoveAll(workDir) 67 | } 68 | } 69 | return err 70 | } 71 | -------------------------------------------------------------------------------- /util/message.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bufio" 5 | "log" 6 | "os" 7 | 8 | "github.com/cubicdaiya/nginx-build/command" 9 | ) 10 | 11 | func PrintFatalMsg(err error, path string) { 12 | if command.VerboseEnabled { 13 | log.Fatal(err) 14 | } 15 | 16 | f, err2 := os.Open(path) 17 | if err2 != nil { 18 | log.Printf("error-log: %s is not found\n", path) 19 | log.Fatal(err) 20 | } 21 | defer f.Close() 22 | 23 | scanner := bufio.NewScanner(f) 24 | for scanner.Scan() { 25 | os.Stderr.Write(scanner.Bytes()) 26 | os.Stderr.Write([]byte("\n")) 27 | } 28 | 29 | log.Fatal(err) 30 | } 31 | -------------------------------------------------------------------------------- /util/patch.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "sync" 9 | 10 | "github.com/cubicdaiya/nginx-build/command" 11 | ) 12 | 13 | var ( 14 | mutex sync.Mutex 15 | patched bool 16 | ) 17 | 18 | func init() { 19 | patched = false 20 | } 21 | 22 | func patch(path, option string, reverse bool) error { 23 | 24 | if reverse { 25 | mutex.Lock() 26 | if patched { 27 | mutex.Unlock() 28 | return nil 29 | } 30 | patched = true 31 | mutex.Unlock() 32 | } 33 | 34 | args := []string{"sh", "-c"} 35 | body := "" 36 | if reverse { 37 | body = fmt.Sprintf("patch %s -R < %s", option, path) 38 | } else { 39 | body = fmt.Sprintf("patch %s < %s", option, path) 40 | } 41 | args = append(args, body) 42 | 43 | cmd, err := command.Make(args) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | // As the output of patch is interactive, 49 | // the result is always output. 50 | cmd.Stdout = os.Stdout 51 | cmd.Stderr = os.Stderr 52 | 53 | return cmd.Run() 54 | } 55 | 56 | func Patch(path, option, root string, reverse bool) { 57 | if path == "" { 58 | return 59 | } 60 | 61 | var pathes []string 62 | if strings.Contains(path, ",") { 63 | pathes = strings.Split(path, ",") 64 | } else { 65 | pathes = append(pathes, path) 66 | } 67 | 68 | // replace directories with all files they contain (recursively) 69 | var expanded_paths []string 70 | for _, path := range pathes { 71 | if !strings.HasPrefix(path, "/") { 72 | path = fmt.Sprintf("%s/%s", root, path) 73 | } 74 | 75 | isDir, err := IsDirectory(path) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | if isDir { 80 | paths, err := ListDirectory(path) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | if paths != nil { 85 | expanded_paths = append(expanded_paths, paths...) 86 | } 87 | } else { 88 | expanded_paths = append(expanded_paths, path) 89 | } 90 | } 91 | 92 | pathes = expanded_paths 93 | 94 | for _, path := range pathes { 95 | if FileExists(path) { 96 | if reverse { 97 | log.Printf("Reverting patch: %s", path) 98 | } else { 99 | log.Printf("Applying patch: %s %s", option, path) 100 | } 101 | if err := patch(path, option, reverse); err != nil { 102 | log.Fatalf("Failed to apply patch: %s %s", option, path) 103 | } 104 | } else { 105 | log.Fatalf("Patch pathname: %s is not found", path) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/cubicdaiya/nginx-build/builder" 8 | ) 9 | 10 | func versionsGenNginx() []string { 11 | return []string{ 12 | fmt.Sprintf("nginx-%s", builder.NginxVersion), 13 | } 14 | } 15 | 16 | func versionsGenOpenResty() []string { 17 | return []string{ 18 | fmt.Sprintf("openresty-%s", builder.OpenRestyVersion), 19 | } 20 | } 21 | 22 | func versionsGenFreenginx() []string { 23 | return []string{ 24 | fmt.Sprintf("freenginx-%s", builder.FreenginxVersion), 25 | } 26 | } 27 | 28 | func printNginxVersions() { 29 | var versions []string 30 | versions = append(versions, versionsGenNginx()...) 31 | versions = append(versions, versionsGenOpenResty()...) 32 | versions = append(versions, versionsGenFreenginx()...) 33 | for _, v := range versions { 34 | fmt.Println(v) 35 | } 36 | } 37 | 38 | func versionCheck(version string) { 39 | if len(version) == 0 { 40 | log.Println("[warn]nginx version is not set.") 41 | log.Printf("[warn]nginx-build use %s.\n", builder.NginxVersion) 42 | } 43 | } 44 | --------------------------------------------------------------------------------