Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add egbuilder #1058

Merged
merged 16 commits into from
Aug 15, 2023
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ endif
# Targets
TARGET_SERVER=${RELEASE_DIR}/easegress-server
TARGET_CLIENT=${RELEASE_DIR}/egctl
TARGET_BUILDER=${RELEASE_DIR}/egbuilder

# Rules
build: build_client build_server
build: build_client build_server build_builder

wasm: ENABLE_CGO=CGO_ENABLED=1
wasm: GO_BUILD_TAGS=-tags wasmhost
Expand All @@ -63,6 +64,12 @@ build_client:
CGO_ENABLED=0 go build -v -trimpath -ldflags ${GO_LD_FLAGS} \
-o ${TARGET_CLIENT} ${MKFILE_DIR}cmd/client

build_builder:
@echo "build builder"
cd ${MKFILE_DIR} && \
CGO_ENABLED=0 go build -v -trimpath -ldflags ${GO_LD_FLAGS} \
-o ${TARGET_BUILDER} ${MKFILE_DIR}cmd/builder

build_server:
@echo "build server"
cd ${MKFILE_DIR} && \
Expand Down Expand Up @@ -101,7 +108,7 @@ test:
go mod tidy
git diff --exit-code go.mod go.sum
go mod verify
go test -v -gcflags "all=-l" ${MKFILE_DIR}pkg/... ${TEST_FLAGS}
go test -v -gcflags "all=-l" ${MKFILE_DIR}pkg/... ${MKFILE_DIR}cmd/... ${TEST_FLAGS}

integration_test: build
{ \
Expand Down
101 changes: 101 additions & 0 deletions cmd/builder/build/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed 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:https://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.
*/

// Package build contains utils for egbuilder build command.
package build

import (
"context"
"fmt"
"os"
"strings"
)

// Build builds Easegress with custom plugins.
func Build(ctx context.Context, config *Config) error {
// prepare the build environment
fmt.Println("Preparing build environment...")
buildEnv, err := newEnvironment(ctx, config)
if err != nil {
return err
}
defer buildEnv.Close()
fmt.Println("Build environment is ready.")

if config.SkipBuild {
fmt.Println("Flag skipBuild is set, skip building easegress-server.")
return nil
}

// prepare the environment for the go command; for
// the most part we want it to inherit our current
// environment, with a few customizations
env := os.Environ()
compile := config.Compile
env = setEnv(env, "GOOS="+compile.OS)
env = setEnv(env, "GOARCH="+compile.Arch)
env = setEnv(env, "GOARM="+compile.ARM)
if config.RaceDetector && !config.Compile.Cgo {
fmt.Println("enabling cgo because it is required by the race detector")
config.Compile.Cgo = true
}
env = setEnv(env, fmt.Sprintf("CGO_ENABLED=%s", config.Compile.CgoEnabled()))

fmt.Println("Building easegress-server...")
// // tidy the module to ensure go.mod and go.sum are consistent with the module prereq
tidyCmd := buildEnv.newGoCmdWithModFlags(ctx, "mod", "tidy", "-e")
if err := tidyCmd.Run(); err != nil {
return err
}

// compile
cmd := buildEnv.newGoCmdWithBuildFlags(ctx, "build", "-o", config.Output)
if len(config.BuildFlags) == 0 {
cmd.Args = append(cmd.Args,
"-ldflags", "-w -s",
"-trimpath",
)
}

if config.RaceDetector {
cmd.Args = append(cmd.Args, "-race")
}
cmd.Env = env
err = cmd.Run()
if err != nil {
return err
}
fmt.Printf("Build complete: %s\n", config.Output)
return nil
}

// setEnv sets an environment variable-value pair in
// env, overriding an existing variable if it already
// exists. The env slice is one such as is returned
// by os.Environ(), and set must also have the form
// of key=value.
func setEnv(env []string, set string) []string {
parts := strings.SplitN(set, "=", 2)
key := parts[0]
for i := 0; i < len(env); i++ {
if strings.HasPrefix(env[i], key+"=") {
env[i] = set
return env
}
}
return append(env, set)
}
154 changes: 154 additions & 0 deletions cmd/builder/build/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed 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:https://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.
*/

// Package build contains utils for egbuilder build command.
package build

import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/megaease/easegress/v2/pkg/util/codectool"
)

type Options struct {
Compile Compile `json:"compile"`

EGVersion string `json:"egVersion"`
RaceDetector bool `json:"raceDetector"`
SkipBuild bool `json:"skipBuild"`
SkipCleanup bool `json:"skipCleanup"`

BuildFlags []string `json:"buildFlags"`
ModFlags []string `json:"modFlags"`
}

type Config struct {
Options `json:",inline"`

Plugins []*Plugin `json:"plugins"`
Output string `json:"output"`
}

// Plugin contains parameters for a plugin.
type Plugin struct {
Module string `json:"module"`
Version string `json:"version"`
Replacement string `json:"replacement"`
}

// Compile contains parameters for compilation.
type Compile struct {
OS string `json:"os,omitempty"`
Arch string `json:"arch,omitempty"`
ARM string `json:"arm,omitempty"`
Cgo bool `json:"cgo,omitempty"`
}

// CgoEnabled returns "1" if c.Cgo is true, "0" otherwise.
// This is used for setting the CGO_ENABLED env variable.
func (c Compile) CgoEnabled() string {
if c.Cgo {
return "1"
}
return "0"
}

// Init initializes the config. When use NewConfig, Init will be called automatically.
func (config *Config) Init() error {
// check plugins
for _, plugin := range config.Plugins {
if plugin.Module == "" {
return fmt.Errorf("empty module name in plugins")
}
if strings.HasPrefix(plugin.Replacement, ".") {
repl, err := filepath.Abs(plugin.Replacement)
if err != nil {
return fmt.Errorf("get absolute path of %s in module %s failed: %v", plugin.Replacement, plugin.Module, err)
}
plugin.Replacement = repl
}
}

// check output
if config.Output == "" {
output, err := getBuildOutputFile()
if err != nil {
return fmt.Errorf("get build output file failed: %v", err)
}
config.Output = output
}
if strings.HasPrefix(config.Output, ".") {
output, err := filepath.Abs(config.Output)
if err != nil {
return fmt.Errorf("get absolute path of output file %s failed: %v", config.Output, err)
}
config.Output = output
}

// check compile
if config.Compile.OS == "" {
config.Compile.OS = os.Getenv("GOOS")
}
if config.Compile.Arch == "" {
config.Compile.Arch = os.Getenv("GOARCH")
}
if config.Compile.ARM == "" {
config.Compile.ARM = os.Getenv("GOARM")
}

fmt.Printf("Build easegress-server with config\n")
fmt.Printf(" %#v\n", config.Options)
for _, p := range config.Plugins {
fmt.Printf(" plugin: %#v\n", p)
}
fmt.Printf(" output: %s\n", config.Output)
return nil
}

func NewConfig(filename string) (*Config, error) {
config := &Config{}
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("read file %s failed: %v", filename, err)
}
err = codectool.UnmarshalYAML(data, config)
if err != nil {
return nil, fmt.Errorf("unmarshal yaml file %s failed: %v", filename, err)
}

err = config.Init()
if err != nil {
return nil, fmt.Errorf("init config failed: %v", err)
}
return config, nil
}

func getBuildOutputFile() (string, error) {
cwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("get current working directory failed: %v", err)
}
output := filepath.Join(cwd, "easegress-server")
if runtime.GOOS == "windows" {
output += ".exe"
}
return output, nil
}
Loading
Loading