make 命令执行时,需要一个 makefile
文件,以告诉 make
命令需要怎么样的去编译和链接程序。
makefile的规则
在讲述这个 makefile
之前,还是让我们先来粗略地看一看 makefile
的规则。
target ... : prerequisites ...
command
...
...
target
可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites
生成该 target 所依赖的文件和/
或target
command
该target要执行的命令(任意的shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于 prerequisites
中的文件,其生成规则定义在 command
中。说白一点就是说:
prerequisites 中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
这就是makefile的规则,也就是makefile中最核心的内容。
Mage
Mage是一个使用Go的类似 make/rake
的构建工具。你编写普通的 Go
函数,Mage
会自动将它们作为类似Makefile
的可运行目标。
使用说明
mage [options] [target]
Mage is a make-like command runner. See https://magefile.org for full docs.
Commands:
-clean clean out old generated binaries from CACHE_DIR
-compile <string>
output a static binary to the given path
-h show this help
-init create a starting template if no mage files exist
-l list mage targets in this directory
-version show version info for the mage binary
Options:
-d <string>
directory to read magefiles from (default ".")
-debug turn on debug messages
-f force recreation of compiled magefile
-goarch sets the GOARCH for the binary created by -compile (default: current arch)
-gocmd <string>
use the given go binary to compile the output (default: "go")
-goos sets the GOOS for the binary created by -compile (default: current OS)
-h show description of a target
-keep keep intermediate mage files around after running
-t <string>
timeout in duration parsable format (e.g. 5m30s)
-v show verbose output when running mage targets
-w <string>
working directory where magefiles will run (default -d value)
示例
示例来源 elastic
的 beats 项目。
D:\Projects\Github\elastic\beats\filebeat\Makefile
BEAT_NAME?=filebeat
BEAT_TITLE?=Filebeat
SYSTEM_TESTS?=true
TEST_ENVIRONMENT?=true
GOX_FLAGS=-arch="amd64 386 arm ppc64 ppc64le"
ES_BEATS?=..
EXCLUDE_COMMON_UPDATE_TARGET=true
include ${ES_BEATS}/libbeat/scripts/Makefile
.PHONY: update
update: mage
mage update
# Creates a new module. Requires the params MODULE.
.PHONY: create-module
create-module: mage
mage generate:module
# Creates a new fileset. Requires the params MODULE and FILESET.
.PHONY: create-fileset
create-fileset: mage
mage generate:fileset
# Creates a fields.yml based on a pipeline.json file. Requires the params MODULE and FILESET.
.PHONY: create-fields
create-fields: mage
mage generate:fields
D:\Projects\Github\elastic\beats\filebeat\magefile.go
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//go:build mage
// +build mage
package main
import (
"context"
"fmt"
"time"
"github.com/magefile/mage/mg"
devtools "github.com/elastic/beats/v7/dev-tools/mage"
filebeat "github.com/elastic/beats/v7/filebeat/scripts/mage"
// mage:import
"github.com/elastic/beats/v7/dev-tools/mage/target/common"
// mage:import generate
_ "github.com/elastic/beats/v7/filebeat/scripts/mage/generate"
// mage:import
_ "github.com/elastic/beats/v7/dev-tools/mage/target/unittest"
// mage:import
"github.com/elastic/beats/v7/dev-tools/mage/target/test"
)
func init() {
common.RegisterCheckDeps(Update)
test.RegisterDeps(IntegTest)
devtools.BeatDescription = "Filebeat sends log files to Logstash or directly to Elasticsearch."
}
// Build builds the Beat binary.
func Build() error {
return devtools.Build(devtools.DefaultBuildArgs())
}
// GolangCrossBuild builds the Beat binary inside the golang-builder.
// Do not use directly, use crossBuild instead.
func GolangCrossBuild() error {
return filebeat.GolangCrossBuild()
}
// BuildGoDaemon builds the go-daemon binary (use crossBuildGoDaemon).
func BuildGoDaemon() error {
return devtools.BuildGoDaemon()
}
// CrossBuild cross-builds the beat for all target platforms.
func CrossBuild() error {
return filebeat.CrossBuild()
}
// CrossBuildGoDaemon cross-builds the go-daemon binary using Docker.
func CrossBuildGoDaemon() error {
return devtools.CrossBuildGoDaemon()
}
// Package packages the Beat for distribution.
// Use SNAPSHOT=true to build snapshots.
// Use PLATFORMS to control the target platforms.
// Use VERSION_QUALIFIER to control the version qualifier.
func Package() {
start := time.Now()
defer func() { fmt.Println("package ran for", time.Since(start)) }()
devtools.UseElasticBeatOSSPackaging()
devtools.PackageKibanaDashboardsFromBuildDir()
filebeat.CustomizePackaging()
mg.Deps(Update)
mg.Deps(CrossBuild, CrossBuildGoDaemon)
mg.SerialDeps(devtools.Package, TestPackages)
}
// TestPackages tests the generated packages (i.e. file modes, owners, groups).
func TestPackages() error {
return devtools.TestPackages(devtools.WithModules(), devtools.WithModulesD())
}
// Update is an alias for executing fields, dashboards, config, includes.
func Update() {
mg.SerialDeps(Fields, Dashboards, Config, includeList, fieldDocs,
filebeat.CollectDocs,
filebeat.PrepareModulePackagingOSS)
}
// Config generates both the short/reference/docker configs and populates the
// modules.d directory.
func Config() {
mg.Deps(devtools.GenerateDirModulesD, configYML)
mg.SerialDeps(devtools.ValidateDirModulesD, devtools.ValidateDirModulesDDatasetsDisabled)
}
func configYML() error {
return devtools.Config(devtools.AllConfigTypes, filebeat.OSSConfigFileParams(), ".")
}
// includeList generates include/list.go with imports for inputs.
func includeList() error {
options := devtools.DefaultIncludeListOptions()
options.ImportDirs = []string{"input/*"}
return devtools.GenerateIncludeListGo(options)
}
// Fields generates fields.yml and fields.go files for the Beat.
func Fields() {
mg.Deps(libbeatAndFilebeatCommonFieldsGo, moduleFieldsGo)
mg.Deps(fieldsYML)
}
// libbeatAndFilebeatCommonFieldsGo generates a fields.go containing both
// libbeat and filebeat's common fields.
func libbeatAndFilebeatCommonFieldsGo() error {
if err := devtools.GenerateFieldsYAML(); err != nil {
return err
}
return devtools.GenerateAllInOneFieldsGo()
}
// moduleFieldsGo generates a fields.go for each module.
func moduleFieldsGo() error {
return devtools.GenerateModuleFieldsGo("module")
}
// fieldsYML generates the fields.yml file containing all fields.
func fieldsYML() error {
return devtools.GenerateFieldsYAML("module")
}
// fieldDocs generates docs/fields.asciidoc containing all fields
// (including x-pack).
func fieldDocs() error {
inputs := []string{
devtools.OSSBeatDir("module"),
devtools.XPackBeatDir("module"),
devtools.OSSBeatDir("input"),
devtools.XPackBeatDir("input"),
devtools.XPackBeatDir("processors"),
}
output := devtools.CreateDir("build/fields/fields.all.yml")
if err := devtools.GenerateFieldsYAMLTo(output, inputs...); err != nil {
return err
}
return devtools.Docs.FieldDocs(output)
}
// Dashboards collects all the dashboards and generates index patterns.
func Dashboards() error {
return devtools.KibanaDashboards("module")
}
// ExportDashboard exports a dashboard and writes it into the correct directory.
//
// Required environment variables:
// - MODULE: Name of the module
// - ID: Dashboard id
func ExportDashboard() error {
return devtools.ExportDashboard()
}
// IntegTest executes integration tests (it uses Docker to run the tests).
func IntegTest() {
mg.SerialDeps(GoIntegTest, PythonIntegTest)
}
// GoIntegTest executes the Go integration tests.
// Use TEST_COVERAGE=true to enable code coverage profiling.
// Use RACE_DETECTOR=true to enable the race detector.
func GoIntegTest(ctx context.Context) error {
runner, err := devtools.NewDockerIntegrationRunner()
if err != nil {
return err
}
return runner.Test("goIntegTest", func() error {
return devtools.GoTest(ctx, devtools.DefaultGoTestIntegrationArgs())
})
}
// PythonIntegTest executes the python system tests in the integration environment (Docker).
// Use GENERATE=true to generate expected log files.
// Use TESTING_FILEBEAT_MODULES=module[,module] to limit what modules to test.
// Use TESTING_FILEBEAT_FILESETS=fileset[,fileset] to limit what fileset to test.
func PythonIntegTest(ctx context.Context) error {
if !devtools.IsInIntegTestEnv() {
mg.Deps(Fields, Dashboards)
}
runner, err := devtools.NewDockerIntegrationRunner(append(devtools.ListMatchingEnvVars("TESTING_FILEBEAT_", "PYTEST_"), "GENERATE")...)
if err != nil {
return err
}
return runner.Test("pythonIntegTest", func() error {
mg.Deps(devtools.BuildSystemTestBinary)
args := devtools.DefaultPythonTestIntegrationArgs()
args.Env["MODULES_PATH"] = devtools.CWD("module")
return devtools.PythonTest(args)
})
}
PS D:\Projects\Github\elastic\beats\filebeat> ./make -debug update
D:\Projects\Go\bin\mage
D:\Projects\Go\bin\mage.exe
参考链接
https://seisman.github.io/how-to-write-makefile/introduction.html