cgo: support full cgo tags
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
#cgo windows,!amd64 CFLAGS: -D_WIN32
|
||||||
|
#cgo !windows CFLAGS: -D_POSIX
|
||||||
|
#cgo windows,amd64 CFLAGS: -D_WIN64
|
||||||
|
#cgo linux,amd64 CFLAGS: -D_LINUX64
|
||||||
|
#cgo !windows,amd64 CFLAGS: -D_UNIX64
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "foo.h"
|
#include "foo.h"
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -50,6 +55,21 @@ static void test_macros() {
|
|||||||
#ifdef BAR
|
#ifdef BAR
|
||||||
printf("BAR is defined\n");
|
printf("BAR is defined\n");
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
printf("WIN32 is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _POSIX
|
||||||
|
printf("POSIX is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN64
|
||||||
|
printf("WIN64 is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _LINUX64
|
||||||
|
printf("LINUX64 is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _UNIX64
|
||||||
|
printf("UNIX64 is defined\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|||||||
@@ -799,10 +799,6 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
|||||||
}
|
}
|
||||||
switch v := instr.(type) {
|
switch v := instr.(type) {
|
||||||
case *ssa.Store:
|
case *ssa.Store:
|
||||||
// skip cgo global variables
|
|
||||||
if checkCgo(v.Addr.Name()) {
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
va := v.Addr
|
va := v.Addr
|
||||||
if va, ok := va.(*ssa.IndexAddr); ok {
|
if va, ok := va.(*ssa.IndexAddr); ok {
|
||||||
if args, ok := p.isVArgs(va.X); ok { // varargs: this is a varargs store
|
if args, ok := p.isVArgs(va.X); ok { // varargs: this is a varargs store
|
||||||
@@ -1083,10 +1079,6 @@ func processPkg(ctx *context, ret llssa.Package, pkg *ssa.Package) {
|
|||||||
case *ssa.Type:
|
case *ssa.Type:
|
||||||
ctx.compileType(ret, member)
|
ctx.compileType(ret, member)
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
// skip cgo global variables
|
|
||||||
if checkCgo(member.Name()) {
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
ctx.compileGlobal(ret, member)
|
ctx.compileGlobal(ret, member)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -438,13 +438,10 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin
|
|||||||
if checkCgo(fname) {
|
if checkCgo(fname) {
|
||||||
return nil, fname, llgoInstr
|
return nil, fname, llgoInstr
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(fname, "_Cfunc_") {
|
if isCgoCfunc(fn) {
|
||||||
if _, ok := llgoInstrs[fname]; ok {
|
if _, ok := llgoInstrs[fname]; ok {
|
||||||
return nil, fname, llgoInstr
|
return nil, fname, llgoInstr
|
||||||
}
|
}
|
||||||
// fname = fname[7:]
|
|
||||||
// fn.WriteTo(os.Stdout)
|
|
||||||
// return nil, fname, cFunc
|
|
||||||
}
|
}
|
||||||
if fnPkg := fn.Pkg; fnPkg != nil {
|
if fnPkg := fn.Pkg; fnPkg != nil {
|
||||||
pkg = fnPkg.Pkg
|
pkg = fnPkg.Pkg
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ func Do(args []string, conf *Config) {
|
|||||||
env := llvm.New("")
|
env := llvm.New("")
|
||||||
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows
|
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows
|
||||||
|
|
||||||
ctx := &context{env, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0}
|
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0}
|
||||||
pkgs := buildAllPkgs(ctx, initial, verbose)
|
pkgs := buildAllPkgs(ctx, initial, verbose)
|
||||||
|
|
||||||
var llFiles []string
|
var llFiles []string
|
||||||
@@ -249,6 +249,7 @@ const (
|
|||||||
|
|
||||||
type context struct {
|
type context struct {
|
||||||
env *llvm.Env
|
env *llvm.Env
|
||||||
|
conf *packages.Config
|
||||||
progSSA *ssa.Program
|
progSSA *ssa.Program
|
||||||
prog llssa.Program
|
prog llssa.Program
|
||||||
dedup packages.Deduper
|
dedup packages.Deduper
|
||||||
@@ -520,7 +521,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoParts []string, er
|
|||||||
cl.SetDebug(0)
|
cl.SetDebug(0)
|
||||||
}
|
}
|
||||||
check(err)
|
check(err)
|
||||||
cgoParts, err = parseCgo(ctx, aPkg, aPkg.Package.Syntax, externs, verbose)
|
cgoParts, err = buildCgo(ctx, aPkg, aPkg.Package.Syntax, externs, verbose)
|
||||||
if needLLFile(ctx.mode) {
|
if needLLFile(ctx.mode) {
|
||||||
pkg.ExportFile += ".ll"
|
pkg.ExportFile += ".ll"
|
||||||
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
|
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
|
||||||
|
|||||||
@@ -26,10 +26,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/buildtags"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cgoDecl struct {
|
type cgoDecl struct {
|
||||||
platform string
|
tag string
|
||||||
cflags string
|
cflags string
|
||||||
ldflags string
|
ldflags string
|
||||||
}
|
}
|
||||||
@@ -48,17 +50,30 @@ static void* _Cmalloc(size_t size) {
|
|||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string][]string, verbose bool) (cgoParts []string, err error) {
|
func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string][]string, verbose bool) (cgoParts []string, err error) {
|
||||||
cfiles, preambles, cdecls, err := parseCgo_(pkg, files)
|
cfiles, preambles, cdecls, err := parseCgo_(pkg, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tagUsed := make(map[string]bool)
|
||||||
|
for _, cdecl := range cdecls {
|
||||||
|
if cdecl.tag != "" {
|
||||||
|
tagUsed[cdecl.tag] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildtags.CheckTags(ctx.conf.BuildFlags, tagUsed)
|
||||||
cflags := []string{}
|
cflags := []string{}
|
||||||
ldflags := []string{}
|
ldflags := []string{}
|
||||||
for _, cdecl := range cdecls {
|
for _, cdecl := range cdecls {
|
||||||
|
if cdecl.tag == "" || tagUsed[cdecl.tag] {
|
||||||
|
if cdecl.cflags != "" {
|
||||||
cflags = append(cflags, cdecl.cflags)
|
cflags = append(cflags, cdecl.cflags)
|
||||||
|
}
|
||||||
|
if cdecl.ldflags != "" {
|
||||||
ldflags = append(ldflags, cdecl.ldflags)
|
ldflags = append(ldflags, cdecl.ldflags)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
incDirs := make(map[string]none)
|
incDirs := make(map[string]none)
|
||||||
for _, preamble := range preambles {
|
for _, preamble := range preambles {
|
||||||
dir, _ := filepath.Split(preamble.goFile)
|
dir, _ := filepath.Split(preamble.goFile)
|
||||||
@@ -95,7 +110,7 @@ func parseCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string
|
|||||||
return nil, fmt.Errorf("failed to create temp file: %v", err)
|
return nil, fmt.Errorf("failed to create temp file: %v", err)
|
||||||
}
|
}
|
||||||
tmpName := tmpFile.Name()
|
tmpName := tmpFile.Name()
|
||||||
// defer os.Remove(tmpName)
|
defer os.Remove(tmpName)
|
||||||
code := cgoHeader + "\n\n" + preamble.src
|
code := cgoHeader + "\n\n" + preamble.src
|
||||||
externDecls := genExternDeclsByClang(code, cflags, cgoFuncs)
|
externDecls := genExternDeclsByClang(code, cflags, cgoFuncs)
|
||||||
if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil {
|
if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil {
|
||||||
@@ -136,38 +151,29 @@ func genExternDeclsByClang(src string, cflags []string, cgoFuncs map[string]stri
|
|||||||
if err := json.Unmarshal(output, &astRoot); err != nil {
|
if err := json.Unmarshal(output, &astRoot); err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
// Extract just function names
|
|
||||||
funcNames := make(map[string]bool)
|
funcNames := make(map[string]bool)
|
||||||
extractFuncNames(&astRoot, funcNames)
|
extractFuncNames(&astRoot, funcNames)
|
||||||
|
|
||||||
b := strings.Builder{}
|
b := strings.Builder{}
|
||||||
// Create a list of functions to remove
|
|
||||||
var toRemove []string
|
var toRemove []string
|
||||||
// Process cgoFuncs and build assignments
|
|
||||||
for cgoFunc, funcName := range cgoFuncs {
|
for cgoFunc, funcName := range cgoFuncs {
|
||||||
if funcNames[funcName] {
|
if funcNames[funcName] {
|
||||||
// Only generate the assignment, not the extern declaration
|
|
||||||
b.WriteString(fmt.Sprintf("void* %s = (void*)%s;\n", cgoFunc, funcName))
|
b.WriteString(fmt.Sprintf("void* %s = (void*)%s;\n", cgoFunc, funcName))
|
||||||
// Mark this function for removal
|
|
||||||
toRemove = append(toRemove, cgoFunc)
|
toRemove = append(toRemove, cgoFunc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove processed functions from cgoFuncs
|
|
||||||
for _, funcName := range toRemove {
|
for _, funcName := range toRemove {
|
||||||
delete(cgoFuncs, funcName)
|
delete(cgoFuncs, funcName)
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simplified function to just collect function names
|
|
||||||
func extractFuncNames(node *clangASTNode, funcNames map[string]bool) {
|
func extractFuncNames(node *clangASTNode, funcNames map[string]bool) {
|
||||||
if node.Kind == "FunctionDecl" && node.Name != "" {
|
for _, inner := range node.Inner {
|
||||||
// Skip functions that are likely internal/system functions
|
if inner.Kind == "FunctionDecl" && inner.Name != "" {
|
||||||
funcNames[node.Name] = true
|
funcNames[inner.Name] = true
|
||||||
}
|
}
|
||||||
// Recursively process inner nodes
|
|
||||||
for i := range node.Inner {
|
|
||||||
extractFuncNames(&node.Inner[i], funcNames)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,18 +257,29 @@ func parseCgoDecl(line string) (cgoDecls []cgoDecl, err error) {
|
|||||||
err = fmt.Errorf("invalid cgo format: %v", line)
|
err = fmt.Errorf("invalid cgo format: %v", line)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decl := strings.TrimSpace(line[:idx])
|
decl := strings.TrimSpace(line[:idx])
|
||||||
arg := strings.TrimSpace(line[idx+1:])
|
arg := strings.TrimSpace(line[idx+1:])
|
||||||
toks := strings.Split(decl, " ")
|
|
||||||
var platform, flag string
|
// Split on first space to remove #cgo
|
||||||
if len(toks) == 2 {
|
parts := strings.SplitN(decl, " ", 2)
|
||||||
flag = toks[1]
|
if len(parts) < 2 {
|
||||||
} else if len(toks) == 3 {
|
err = fmt.Errorf("invalid cgo directive: %v", line)
|
||||||
platform, flag = toks[1], toks[2]
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("invalid cgo directive: %v, toks: %v", line, toks)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process remaining part
|
||||||
|
remaining := strings.TrimSpace(parts[1])
|
||||||
|
var tag, flag string
|
||||||
|
|
||||||
|
// Split on last space to get flag
|
||||||
|
if lastSpace := strings.LastIndex(remaining, " "); lastSpace != -1 {
|
||||||
|
tag = strings.TrimSpace(remaining[:lastSpace])
|
||||||
|
flag = strings.TrimSpace(remaining[lastSpace+1:])
|
||||||
|
} else {
|
||||||
|
flag = remaining
|
||||||
|
}
|
||||||
|
|
||||||
switch flag {
|
switch flag {
|
||||||
case "pkg-config":
|
case "pkg-config":
|
||||||
ldflags, e := exec.Command("pkg-config", "--libs", arg).Output()
|
ldflags, e := exec.Command("pkg-config", "--libs", arg).Output()
|
||||||
@@ -276,18 +293,18 @@ func parseCgoDecl(line string) (cgoDecls []cgoDecl, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
cgoDecls = append(cgoDecls, cgoDecl{
|
cgoDecls = append(cgoDecls, cgoDecl{
|
||||||
platform: platform,
|
tag: tag,
|
||||||
cflags: strings.TrimSpace(string(cflags)),
|
cflags: strings.TrimSpace(string(cflags)),
|
||||||
ldflags: strings.TrimSpace(string(ldflags)),
|
ldflags: strings.TrimSpace(string(ldflags)),
|
||||||
})
|
})
|
||||||
case "CFLAGS":
|
case "CFLAGS":
|
||||||
cgoDecls = append(cgoDecls, cgoDecl{
|
cgoDecls = append(cgoDecls, cgoDecl{
|
||||||
platform: platform,
|
tag: tag,
|
||||||
cflags: arg,
|
cflags: arg,
|
||||||
})
|
})
|
||||||
case "LDFLAGS":
|
case "LDFLAGS":
|
||||||
cgoDecls = append(cgoDecls, cgoDecl{
|
cgoDecls = append(cgoDecls, cgoDecl{
|
||||||
platform: platform,
|
tag: tag,
|
||||||
ldflags: arg,
|
ldflags: arg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
114
internal/buildtags/buildtags.go
Normal file
114
internal/buildtags/buildtags.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 The GoPlus Authors (goplus.org). 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://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 buildtags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkTags checks which build tags are valid by creating virtual test files
|
||||||
|
// and using build.Context.MatchFile to verify them
|
||||||
|
func CheckTags(buildFlags []string, testTags map[string]bool) {
|
||||||
|
buildCtx := build.Default
|
||||||
|
buildCtx.BuildTags = parseBuildTags(buildFlags)
|
||||||
|
|
||||||
|
// Create virtual filesystem
|
||||||
|
vfs := &virtualFS{
|
||||||
|
files: make(map[string]virtualFile),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate virtual files for each test tag
|
||||||
|
i := 0
|
||||||
|
fileToTag := make(map[string]string) // Map to track which file corresponds to which tag
|
||||||
|
for tag := range testTags {
|
||||||
|
fileName := fmt.Sprintf("a%02d.go", i)
|
||||||
|
content := fmt.Sprintf("// +build %s\n\npackage check\n", tag)
|
||||||
|
vfs.files[fileName] = virtualFile{
|
||||||
|
name: fileName,
|
||||||
|
content: content,
|
||||||
|
dir: ".",
|
||||||
|
}
|
||||||
|
fileToTag[fileName] = tag
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override OpenFile to return our virtual file contents
|
||||||
|
buildCtx.OpenFile = func(name string) (io.ReadCloser, error) {
|
||||||
|
if file, ok := vfs.files[name]; ok {
|
||||||
|
return io.NopCloser(strings.NewReader(file.content)), nil
|
||||||
|
}
|
||||||
|
return nil, fs.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each file against build context
|
||||||
|
for fileName, tag := range fileToTag {
|
||||||
|
match, err := buildCtx.MatchFile(".", fileName)
|
||||||
|
if err == nil && match {
|
||||||
|
testTags[tag] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtualFile represents a virtual build tag check file
|
||||||
|
type virtualFile struct {
|
||||||
|
name string
|
||||||
|
content string
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtualFS implements a virtual filesystem for build tag checking
|
||||||
|
type virtualFS struct {
|
||||||
|
files map[string]virtualFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBuildTags(buildFlags []string) []string {
|
||||||
|
buildTags := make([]string, 0)
|
||||||
|
// Extract tags from buildFlags
|
||||||
|
for i := 0; i < len(buildFlags); i++ {
|
||||||
|
flag := buildFlags[i]
|
||||||
|
if flag == "-tags" && i+1 < len(buildFlags) {
|
||||||
|
// Handle "-tags xxx" format
|
||||||
|
tags := strings.FieldsFunc(buildFlags[i+1], func(r rune) bool {
|
||||||
|
return r == ',' || r == ' '
|
||||||
|
})
|
||||||
|
buildTags = append(buildTags, tags...)
|
||||||
|
i++ // Skip the next item since we've processed it
|
||||||
|
} else if strings.HasPrefix(flag, "-tags=") {
|
||||||
|
// Handle "-tags=xxx" format
|
||||||
|
value := strings.TrimPrefix(flag, "-tags=")
|
||||||
|
tags := strings.FieldsFunc(value, func(r rune) bool {
|
||||||
|
return r == ',' || r == ' '
|
||||||
|
})
|
||||||
|
buildTags = append(buildTags, tags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicates from tags
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
uniqueBuildTags := make([]string, 0, len(buildTags))
|
||||||
|
for _, tag := range buildTags {
|
||||||
|
if !seen[tag] {
|
||||||
|
seen[tag] = true
|
||||||
|
uniqueBuildTags = append(uniqueBuildTags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uniqueBuildTags
|
||||||
|
}
|
||||||
153
internal/buildtags/buildtags_test.go
Normal file
153
internal/buildtags/buildtags_test.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package buildtags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckTags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
buildFlags []string
|
||||||
|
testTags map[string]bool
|
||||||
|
want map[string]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "mywindows tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": true,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mywindows,myamd64": runtime.GOARCH == "myamd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-mywindows tags",
|
||||||
|
buildFlags: []string{"-tags", "mylinux"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
"!mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": true,
|
||||||
|
"mylinux,myamd64": runtime.GOARCH == "myamd64",
|
||||||
|
"!mywindows,myamd64": runtime.GOARCH == "myamd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows,myamd64"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"myamd64": false,
|
||||||
|
"mywindows,myamd64": false,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": true,
|
||||||
|
"myamd64": true,
|
||||||
|
"mywindows,myamd64": true,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tags with equals format",
|
||||||
|
buildFlags: []string{"-tags=mywindows,myamd64"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"myamd64": false,
|
||||||
|
"mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": true,
|
||||||
|
"myamd64": true,
|
||||||
|
"mywindows,myamd64": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complex tag combinations",
|
||||||
|
buildFlags: []string{"-tags", "mylinux,myamd64"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mylinux": false,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
"!mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": true,
|
||||||
|
"mylinux": true,
|
||||||
|
"mylinux,myamd64": true,
|
||||||
|
"!mywindows,myamd64": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
testTags := make(map[string]bool)
|
||||||
|
for k := range tt.testTags {
|
||||||
|
testTags[k] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckTags(tt.buildFlags, testTags)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(testTags, tt.want) {
|
||||||
|
t.Errorf("CheckTags() = %v, want %v", testTags, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseBuildTags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
buildFlags []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "space separated tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows myamd64"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equals format",
|
||||||
|
buildFlags: []string{"-tags=mywindows,myamd64"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple -tags flags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows", "-tags", "myamd64"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows myamd64", "-tags=mywindows"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty tags",
|
||||||
|
buildFlags: []string{},
|
||||||
|
want: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := parseBuildTags(tt.buildFlags)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("name: %v, parseBuildTags() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user