Skip to content
This repository has been archived by the owner on May 18, 2024. It is now read-only.

support llvm-mingw for windows #171

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7ac949d
clang/ast: DllImportAttr UnusedAttr for clang-mingw
visualfc Nov 17, 2022
6e477f8
runGoApp use build.ImportDir
visualfc Nov 18, 2022
0672b06
testdata/atomic
visualfc Nov 18, 2022
3e64827
testdata/bitfield
visualfc Nov 18, 2022
a10edeb
testdata/complex
visualfc Nov 18, 2022
26a27a9
testdata/compoundlit
visualfc Nov 18, 2022
8a09507
testdata/flow
visualfc Nov 18, 2022
07c388a
testdata/forceeval
visualfc Nov 18, 2022
c3890f7
testdata/hello
visualfc Nov 18, 2022
e1acf8a
testdata/incdec
visualfc Nov 18, 2022
e8dbea7
testdata/init
visualfc Nov 18, 2022
e585a1c
testdata/operator
visualfc Nov 18, 2022
f2c04b8
testdata/printf
visualfc Nov 18, 2022
cca4965
testdata/strlen
visualfc Nov 18, 2022
9f5e3e1
testdata/struct
visualfc Nov 18, 2022
fdf90bf
testdata/switch
visualfc Nov 18, 2022
0ce145f
testdata/typecast
visualfc Nov 18, 2022
f9e264a
testdata/union
visualfc Nov 18, 2022
e5d337f
testdata/vaexpr
visualfc Nov 18, 2022
d5bba5e
testdata/vappendf
visualfc Nov 18, 2022
b5ae410
testdata/xdigits
visualfc Nov 18, 2022
05758b2
clang/types/parser: fix __attribute__ for func
visualfc Nov 19, 2022
a430529
cl: fix func decl for llvm-mingw
visualfc Nov 19, 2022
7e968fc
testdata/qsort
visualfc Nov 19, 2022
82a1e21
testdata/libc
visualfc Nov 19, 2022
f359d92
c2go -test cleanEndLine for windows
visualfc Nov 19, 2022
599e788
x
visualfc Nov 19, 2022
5aa1012
workflows: llvm-mingw
visualfc Nov 20, 2022
81a8a45
cl: config add ClangTarget to needValist check for windows-msvc
visualfc Nov 21, 2022
87086e2
c2go: init get clang target for build tags and cl.config
visualfc Nov 21, 2022
c4c89d2
testdata: update libc
visualfc Nov 21, 2022
517a160
x
visualfc Nov 22, 2022
b11ef18
testdata
visualfc Nov 22, 2022
7a01450
workflows: windows-latest default
visualfc Nov 22, 2022
5d01b15
testdata/libc
visualfc Nov 22, 2022
0e8808e
c2go: add packages.NewImporter for -tags to fix testdata/libc
visualfc Nov 23, 2022
6ff1ddb
x
visualfc Dec 6, 2022
59e92f2
x
visualfc Jan 2, 2023
efc4123
update go.mod
visualfc Jan 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
go-version: [1.17.x]
os: [ubuntu-latest, macos-11]
os: [ubuntu-latest, macos-11, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
Expand All @@ -29,3 +29,33 @@ jobs:

- name: Codecov
uses: codecov/codecov-action@v2

Test_llvm-mingw:
strategy:
matrix:
go-version: [1.17.x]
os: [windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}

- name: Set up llvm-mingw
run: |
curl -LJO https://github.com/mstorsjo/llvm-mingw/releases/download/20211002/llvm-mingw-20211002-msvcrt-x86_64.zip
unzip -q -d c:\ llvm-mingw-20211002-msvcrt-x86_64.zip

- name: Build
run: go build -v ./...

- name: Test
run: |
$Env:PATH = "c:\llvm-mingw-20211002-msvcrt-x86_64\bin;$Env:PATH"
go test -v -coverprofile="coverage.txt" -covermode=atomic ./...

- name: Codecov
uses: codecov/codecov-action@v2
66 changes: 46 additions & 20 deletions c2go.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"errors"
"fmt"
"go/build"
"io"
"log"
"os"
Expand Down Expand Up @@ -187,7 +188,7 @@ func execFile(pkgname string, outfile string, flags int) {

needPkgInfo := (flags & FlagDepsAutoGen) != 0
pkg, err := cl.NewPackage("", pkgname, doc, &cl.Config{
SrcFile: outfile, NeedPkgInfo: needPkgInfo,
SrcFile: outfile, NeedPkgInfo: needPkgInfo, ClangTarget: clangTarget,
})
check(err)

Expand Down Expand Up @@ -230,6 +231,10 @@ func checkEqual(prompt string, a, expected []byte) {
fatal(errors.New("checkEqual: unexpected " + prompt))
}

func cleanEndLine(data []byte) []byte {
return bytes.ReplaceAll(data, []byte{'\r', '\n'}, []byte{'\n'})
}

func runTest(dir string) {
var goOut, goErr bytes.Buffer
var cOut, cErr bytes.Buffer
Expand All @@ -238,29 +243,27 @@ func runTest(dir string) {
return
}
runCApp(dir, &cOut, &cErr)
checkEqual("output", goOut.Bytes(), cOut.Bytes())
checkEqual("stderr", goErr.Bytes(), cErr.Bytes())
checkEqual("output", goOut.Bytes(), cleanEndLine(cOut.Bytes()))
checkEqual("stderr", goErr.Bytes(), cleanEndLine(cErr.Bytes()))
}

func goFiles(dir string) ([]string, error) {
if dir == "" {
dir = "."
}
ctx := build.Default
ctx.BuildTags = []string{getBuildTags()}
bp, err := ctx.ImportDir(dir, 0)
if err != nil {
return nil, err
}
return append(bp.GoFiles, bp.TestGoFiles...), nil
}

func runGoApp(dir string, stdout, stderr io.Writer, doRunTest bool) (dontRunTest bool) {
files, err := filepath.Glob("*.go")
files, err := goFiles(dir)
check(err)

for i, n := 0, len(files); i < n; i++ {
fname := filepath.Base(files[i])
if pos := strings.LastIndex(fname, "_"); pos >= 0 {
switch os := fname[pos+1 : len(fname)-3]; os {
case "darwin", "linux", "windows":
if os != runtime.GOOS { // skip
n--
files[i], files[n] = files[n], files[i]
files = files[:n]
i--
}
}
}
}

if doRunTest {
for _, file := range files {
if filepath.Base(file) == "main.go" {
Expand Down Expand Up @@ -298,13 +301,15 @@ func runCApp(dir string, stdout, stderr io.Writer) {
}

var (
clangOut = "./a.out"
clangOut = "./a.out"
clangTarget string
)

func init() {
if runtime.GOOS == "windows" {
clangOut = "./a.exe"
}
clangTarget = getClangTarget()
}

func chdir(dir string) string {
Expand Down Expand Up @@ -366,3 +371,24 @@ func getBytes(stdout, stderr io.Writer) (o iBytes, ok bool) {
o, ok = stdout.(iBytes)
return
}

func getClangTarget() string {
cmd := exec.Command("clang", "--version")
data, err := cmd.CombinedOutput()
check(err)
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "Target:") {
return strings.TrimSpace(line[7:])
}
}
return ""
}

func getBuildTags() string {
if strings.HasSuffix(clangTarget, "-windows-msvc") {
return "windows_msvc"
} else if strings.HasSuffix(clangTarget, "-windows-gnu") {
return "windows_gnu"
}
return ""
}
3 changes: 2 additions & 1 deletion cl/blockctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ type blockCtx struct {
curflow flowCtx
bfm BFMode
multiFileCtl
testMain bool
testMain bool
needValist bool
}

func (p *blockCtx) Pkg() *types.Package {
Expand Down
1 change: 1 addition & 0 deletions cl/codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const builtin_decls = `{
"__builtin_inff": "float32 ()",
"__builtin_infl": "float64 ()",
"__builtin_inf": "float64 ()",
"__builtin_llabs": "int64 (int64)",
"__atomic_store_n_u16": "void (uint16*, int, uint16)",
"__atomic_store_n_i16": "void (int16*, int, int16)",
"__atomic_store_n_u32": "void (uint32*, int, uint32)",
Expand Down
11 changes: 9 additions & 2 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"go/types"
"log"
"path/filepath"
"strings"
"syscall"

goast "go/ast"
Expand Down Expand Up @@ -190,6 +191,9 @@ type Config struct {

// TestMain specifies to generate TestMain func as entry, not main func.
TestMain bool

// ClangTarget specifies clang default target by clang --version.
ClangTarget string
}

const (
Expand Down Expand Up @@ -286,6 +290,9 @@ func loadFile(p *gox.Package, conf *Config, file *ast.Node) (pi *PkgInfo, err er
ctx.ignored = append(ctx.ignored, ign)
}
}
if strings.HasSuffix(conf.ClangTarget, "-windows-msvc") {
ctx.needValist = true
}
compileDeclStmt(ctx, file, true)
if conf.NeedPkgInfo {
pkgInfo := ctx.PkgInfo // make a copy: don't keep a ref to blockCtx
Expand Down Expand Up @@ -364,7 +371,7 @@ func compileDeclStmt(ctx *blockCtx, node *ast.Node, global bool) {
compileEnum(ctx, decl, global)
case ast.EmptyDecl:
case ast.FunctionDecl:
if global {
if global || strings.HasPrefix(decl.Name, "__mingw_") {
compileFunc(ctx, decl)
continue
}
Expand Down Expand Up @@ -402,7 +409,7 @@ func compileFunc(ctx *blockCtx, fn *ast.Node) {
ast.AlwaysInlineAttr, ast.WarnUnusedResultAttr, ast.NoThrowAttr, ast.NoInlineAttr, ast.AllocSizeAttr,
ast.NonNullAttr, ast.ConstAttr, ast.PureAttr, ast.GNUInlineAttr, ast.ReturnsTwiceAttr, ast.NoSanitizeAttr,
ast.RestrictAttr, ast.MSAllocatorAttr, ast.VisibilityAttr, ast.C11NoReturnAttr, ast.StrictFPAttr,
ast.AllocAlignAttr, ast.DisableTailCallsAttr:
ast.DLLImportAttr, ast.UnusedAttr, ast.NoDebugAttr, ast.AllocAlignAttr, ast.DisableTailCallsAttr:
default:
log.Panicln("compileFunc: unknown kind =", item.Kind)
}
Expand Down
3 changes: 3 additions & 0 deletions cl/type_and_var.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ func compileTypedef(ctx *blockCtx, decl *ast.Node, global, pub bool) types.Type
return nil
}
}
if name == "va_list" && ctx.needValist {
typ = ctypes.Valist
}
}
if scope == ctx.pkg.Types.Scope() {
ctx.cb.AliasType(name, typ, ctx.goNodePos(decl))
Expand Down
3 changes: 3 additions & 0 deletions clang/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ const (
StringLiteral Kind = "StringLiteral"
FloatingLiteral Kind = "FloatingLiteral"
ImaginaryLiteral Kind = "ImaginaryLiteral"
DLLImportAttr Kind = "DLLImportAttr"
UnusedAttr Kind = "UnusedAttr"
NoDebugAttr Kind = "NoDebugAttr"
AllocAlignAttr Kind = "AllocAlignAttr"
DisableTailCallsAttr Kind = "DisableTailCallsAttr"
StaticAssertDecl Kind = "StaticAssertDecl"
Expand Down
7 changes: 7 additions & 0 deletions clang/types/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,13 @@ func (p *parser) parse(inFlags int) (t types.Type, kind int, err error) {
fallthrough
default:
if t != nil {
if _, ok := t.(*types.Signature); ok {
if p.lit == "__attribute__" {
p.tok, p.lit = token.EOF, ""
p.unget(token.EOF, "")
break
}
}
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType(lit, flags); err != nil {
Expand Down
1 change: 1 addition & 0 deletions clang/types/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ var cases = []testCase{
{qualType: "int (*)(void *, int, const char *, void (**)(void *, int, void **), void **)"},
{qualType: "struct (anonymous) [2]", anonym: tyInt, typ: types.NewArray(tyInt, 2)},
{qualType: "enum a", typ: ctypes.Int},
{qualType: "int (*)(void) __attribute__((__cdecl))", typ: newFn(nil, typesInt)},
}

type baseEnv struct {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ require (
github.com/goplus/mod v0.9.12
github.com/json-iterator/go v1.1.12
github.com/qiniu/x v1.11.9
golang.org/x/tools v0.4.0
)
121 changes: 121 additions & 0 deletions packages/imp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
Copyright 2022 The GoPlus Authors (goplus.org)
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 packages

import (
"bytes"
"errors"
"go/token"
"go/types"
"os"
"os/exec"

"golang.org/x/tools/go/gcexportdata"
)

// ----------------------------------------------------------------------------

type Importer struct {
loaded map[string]*types.Package
fset *token.FileSet
tags string
dir string
}

// NewImporter creates an Importer object that meets types.Importer interface.
func NewImporter(fset *token.FileSet, tags string, workDir ...string) *Importer {
dir := ""
if len(workDir) > 0 {
dir = workDir[0]
}
if fset == nil {
fset = token.NewFileSet()
}
loaded := make(map[string]*types.Package)
loaded["unsafe"] = types.Unsafe
return &Importer{loaded: loaded, fset: fset, tags: tags, dir: dir}
}

func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
return p.ImportFrom(pkgPath, p.dir, 0)
}

// ImportFrom returns the imported package for the given import
// path when imported by a package file located in dir.
// If the import failed, besides returning an error, ImportFrom
// is encouraged to cache and return a package anyway, if one
// was created. This will reduce package inconsistencies and
// follow-on type checker errors due to the missing package.
// The mode value must be 0; it is reserved for future use.
// Two calls to ImportFrom with the same path and dir must
// return the same package.
func (p *Importer) ImportFrom(pkgPath, dir string, mode types.ImportMode) (*types.Package, error) {
if ret, ok := p.loaded[pkgPath]; ok && ret.Complete() {
return ret, nil
}
expfile, err := FindExport(dir, pkgPath, p.tags)
if err != nil {
return nil, err
}
return p.loadByExport(expfile, pkgPath)
}

func (p *Importer) loadByExport(expfile string, pkgPath string) (pkg *types.Package, err error) {
f, err := os.Open(expfile)
if err != nil {
return
}
defer f.Close()

r, err := gcexportdata.NewReader(f)
if err == nil {
pkg, err = gcexportdata.Read(r, p.fset, p.loaded, pkgPath)
}
return
}

// ----------------------------------------------------------------------------

// FindExport lookups export file (.a) of a package by its pkgPath.
// It returns empty if pkgPath not found.
func FindExport(dir, pkgPath string, tags string) (expfile string, err error) {
data, err := golistExport(dir, pkgPath, tags)
if err != nil {
return
}
expfile = string(bytes.TrimSuffix(data, []byte{'\n'}))
return
}

func golistExport(dir, pkgPath string, tags string) (ret []byte, err error) {
var stdout, stderr bytes.Buffer
var args []string = []string{"list"}
if len(tags) > 0 {
args = append(args, "--tags", tags)
}
args = append(args, "-f={{.Export}}", "-export", pkgPath)
cmd := exec.Command("go", args...)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.Dir = dir
err = cmd.Run()
if err == nil {
ret = stdout.Bytes()
} else if stderr.Len() > 0 {
err = errors.New(stderr.String())
}
return
}

// ----------------------------------------------------------------------------
Loading