8
.github/ci-config/codecov.yml
vendored
8
.github/ci-config/codecov.yml
vendored
@@ -1,8 +0,0 @@
|
|||||||
coverage:
|
|
||||||
ignore:
|
|
||||||
- "compiler/chore"
|
|
||||||
- "chore"
|
|
||||||
- "py"
|
|
||||||
- "x"
|
|
||||||
- "cpp"
|
|
||||||
- "runtime"
|
|
||||||
3
.github/codecov.yml
vendored
Normal file
3
.github/codecov.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
coverage:
|
||||||
|
ignore:
|
||||||
|
- "compiler/chore"
|
||||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -54,5 +54,3 @@ jobs:
|
|||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{secrets.CODECOV_TOKEN}}
|
token: ${{secrets.CODECOV_TOKEN}}
|
||||||
slug: goplus/llgo
|
|
||||||
codecov_yml_path: .github/ci-config/codecov.yml
|
|
||||||
|
|||||||
62
compiler/chore/llgen/llgen_test.go
Normal file
62
compiler/chore/llgen/llgen_test.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
// Create test package in current module
|
||||||
|
testPkg := filepath.Join(".testdata_dont_commit", "hello")
|
||||||
|
err := os.MkdirAll(testPkg, 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(filepath.Join(".testdata_dont_commit"))
|
||||||
|
|
||||||
|
helloFile := filepath.Join(testPkg, "hello.go")
|
||||||
|
err = os.WriteFile(helloFile, []byte(`package hello
|
||||||
|
|
||||||
|
func Hello() string {
|
||||||
|
return "Hello, World!"
|
||||||
|
}
|
||||||
|
`), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save original args and restore them after test
|
||||||
|
oldArgs := os.Args
|
||||||
|
defer func() { os.Args = oldArgs }()
|
||||||
|
|
||||||
|
// Get absolute path to test package
|
||||||
|
absTestPkg, err := filepath.Abs(testPkg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set test arguments
|
||||||
|
os.Args = []string{"llgen", absTestPkg}
|
||||||
|
|
||||||
|
// Run main
|
||||||
|
main()
|
||||||
|
|
||||||
|
// Check if the output file exists
|
||||||
|
outputFile := filepath.Join(testPkg, "llgo_autogen.ll")
|
||||||
|
log.Printf("Generated file: %s", filepath.Join(absTestPkg, "llgo_autogen.ll"))
|
||||||
|
if _, err = os.Stat(outputFile); err != nil {
|
||||||
|
t.Fatalf("Generated file should exist: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and verify file content
|
||||||
|
content, err := os.ReadFile(outputFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Should be able to read generated file: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(content), "define") {
|
||||||
|
t.Error("Generated file should contain LLVM IR code")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
package cl_test
|
package cl_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cl"
|
"github.com/goplus/llgo/compiler/cl"
|
||||||
@@ -24,6 +27,13 @@ import (
|
|||||||
"github.com/goplus/llgo/compiler/internal/build"
|
"github.com/goplus/llgo/compiler/internal/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
devNull, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
|
||||||
|
os.Stderr = devNull
|
||||||
|
os.Stdout = devNull
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
func testCompile(t *testing.T, src, expected string) {
|
func testCompile(t *testing.T, src, expected string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cltest.TestCompileEx(t, src, "foo.go", expected, false)
|
cltest.TestCompileEx(t, src, "foo.go", expected, false)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
||||||
"github.com/goplus/llgo/compiler/internal/build"
|
"github.com/goplus/llgo/compiler/internal/build"
|
||||||
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
)
|
)
|
||||||
|
|
||||||
// llgo build
|
// llgo build
|
||||||
@@ -47,6 +48,6 @@ func runCmd(cmd *base.Command, args []string) {
|
|||||||
_, err := build.Do(args, conf)
|
_, err := build.Do(args, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
mockable.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
||||||
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Help implements the 'help' command.
|
// Help implements the 'help' command.
|
||||||
@@ -49,7 +50,7 @@ Args:
|
|||||||
helpSuccess += " " + strings.Join(args[:i], " ")
|
helpSuccess += " " + strings.Join(args[:i], " ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "llgo help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
|
fmt.Fprintf(os.Stderr, "llgo help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
|
||||||
os.Exit(2)
|
mockable.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cmd.Commands) > 0 {
|
if len(cmd.Commands) > 0 {
|
||||||
@@ -98,7 +99,7 @@ func tmpl(w io.Writer, text string, data interface{}) {
|
|||||||
if ew.err != nil {
|
if ew.err != nil {
|
||||||
// I/O error writing. Ignore write on closed pipe.
|
// I/O error writing. Ignore write on closed pipe.
|
||||||
if strings.Contains(ew.err.Error(), "pipe") {
|
if strings.Contains(ew.err.Error(), "pipe") {
|
||||||
os.Exit(1)
|
mockable.Exit(1)
|
||||||
}
|
}
|
||||||
log.Fatalf("writing output: %v", ew.err)
|
log.Fatalf("writing output: %v", ew.err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
||||||
"github.com/goplus/llgo/compiler/internal/build"
|
"github.com/goplus/llgo/compiler/internal/build"
|
||||||
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
)
|
)
|
||||||
|
|
||||||
// llgo install
|
// llgo install
|
||||||
@@ -40,6 +41,6 @@ func runCmd(cmd *base.Command, args []string) {
|
|||||||
_, err := build.Do(args, conf)
|
_, err := build.Do(args, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
mockable.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
||||||
"github.com/goplus/llgo/compiler/internal/build"
|
"github.com/goplus/llgo/compiler/internal/build"
|
||||||
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -68,7 +69,7 @@ func runCmdEx(_ *base.Command, args []string, mode build.Mode) {
|
|||||||
_, err = build.Do(args, conf)
|
_, err = build.Do(args, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
mockable.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
||||||
"github.com/goplus/llgo/x/env"
|
"github.com/goplus/llgo/compiler/internal/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// llgo version
|
// llgo version
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/goplus/llgo/compiler/cmd/internal/install"
|
"github.com/goplus/llgo/compiler/cmd/internal/install"
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/run"
|
"github.com/goplus/llgo/compiler/cmd/internal/run"
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/version"
|
"github.com/goplus/llgo/compiler/cmd/internal/version"
|
||||||
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mainUsage() {
|
func mainUsage() {
|
||||||
@@ -77,7 +78,7 @@ BigCmdLoop:
|
|||||||
bigCmd = cmd
|
bigCmd = cmd
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
help.PrintUsage(os.Stderr, bigCmd)
|
help.PrintUsage(os.Stderr, bigCmd)
|
||||||
os.Exit(2)
|
mockable.Exit(2)
|
||||||
}
|
}
|
||||||
if args[0] == "help" {
|
if args[0] == "help" {
|
||||||
help.Help(os.Stderr, append(strings.Split(base.CmdName, " "), args[1:]...))
|
help.Help(os.Stderr, append(strings.Split(base.CmdName, " "), args[1:]...))
|
||||||
@@ -97,6 +98,6 @@ BigCmdLoop:
|
|||||||
helpArg = " " + base.CmdName[:i]
|
helpArg = " " + base.CmdName[:i]
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "llgo %s: unknown command\nRun 'llgo help%s' for usage.\n", base.CmdName, helpArg)
|
fmt.Fprintf(os.Stderr, "llgo %s: unknown command\nRun 'llgo help%s' for usage.\n", base.CmdName, helpArg)
|
||||||
os.Exit(2)
|
mockable.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,76 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"fmt"
|
||||||
"flag"
|
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/base"
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/build"
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/help"
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/install"
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/run"
|
|
||||||
"github.com/goplus/llgo/compiler/cmd/internal/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupTestProject(t *testing.T) string {
|
func init() {
|
||||||
// Create a temporary directory for the test project
|
origWd, err := os.Getwd()
|
||||||
tmpDir, err := os.MkdirTemp("", "llgo-test-*")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create temp dir: %v", err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a simple Go program
|
// Set LLGO_ROOT to project root
|
||||||
mainFile := filepath.Join(tmpDir, "main.go")
|
llgoRoot := filepath.Join(origWd, "../../..")
|
||||||
err = os.WriteFile(mainFile, []byte(`package main
|
if err := os.Setenv("LLGO_ROOT", llgoRoot); err != nil {
|
||||||
|
panic(fmt.Sprintf("Failed to set LLGO_ROOT: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTestProject(t *testing.T) string {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
|
// Create main.go
|
||||||
|
mainGo := filepath.Join(tmpDir, "main.go")
|
||||||
|
err := os.WriteFile(mainGo, []byte(`package main
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
import "os"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Hello, LLGO!")
|
var arg string = "LLGO"
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
arg = os.Args[1]
|
||||||
|
}
|
||||||
|
switch arg {
|
||||||
|
case "stderr":
|
||||||
|
fmt.Fprintln(os.Stderr, "Hello, World!")
|
||||||
|
case "exit":
|
||||||
|
os.Exit(1)
|
||||||
|
default:
|
||||||
|
fmt.Println("Hello, " + arg + "!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`), 0644)
|
`), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.RemoveAll(tmpDir)
|
t.Fatalf("Failed to create main.go: %v", err)
|
||||||
t.Fatalf("Failed to write main.go: %v", err)
|
}
|
||||||
|
|
||||||
|
// Create llgo.expect for cmptest
|
||||||
|
expectFile := filepath.Join(tmpDir, "llgo.expect")
|
||||||
|
err = os.WriteFile(expectFile, []byte(`#stdout
|
||||||
|
Hello, LLGO!
|
||||||
|
|
||||||
|
#stderr
|
||||||
|
|
||||||
|
#exit 0
|
||||||
|
`), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create llgo.expect: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a go.mod file
|
// Create a go.mod file
|
||||||
goMod := filepath.Join(tmpDir, "go.mod")
|
err = os.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte(`module testproject
|
||||||
err = os.WriteFile(goMod, []byte(`module testproject
|
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
`), 0644)
|
`), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.RemoveAll(tmpDir)
|
|
||||||
t.Fatalf("Failed to write go.mod: %v", err)
|
t.Fatalf("Failed to write go.mod: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,168 +78,195 @@ go 1.20
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestProjectCommands(t *testing.T) {
|
func TestProjectCommands(t *testing.T) {
|
||||||
// Setup test project
|
|
||||||
tmpDir := setupTestProject(t)
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
// Save original working directory and environment
|
|
||||||
origWd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to get current directory: %v", err)
|
|
||||||
}
|
|
||||||
defer os.Chdir(origWd)
|
|
||||||
|
|
||||||
// Change to test project directory
|
|
||||||
if err := os.Chdir(tmpDir); err != nil {
|
|
||||||
t.Fatalf("Failed to change to test directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
cmd *base.Command
|
|
||||||
args []string
|
args []string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
|
setup func(dir string) error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "build command",
|
name: "build command",
|
||||||
cmd: build.Cmd,
|
args: []string{"llgo", "build", "."},
|
||||||
args: []string{"."},
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "install command",
|
name: "install command",
|
||||||
cmd: install.Cmd,
|
args: []string{"llgo", "install", "."},
|
||||||
args: []string{"."},
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "run command",
|
name: "run command",
|
||||||
cmd: run.Cmd,
|
args: []string{"llgo", "run", "."},
|
||||||
args: []string{"main.go"},
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "run command with file",
|
||||||
|
args: []string{"llgo", "run", "main.go"},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "run command verbose",
|
||||||
|
args: []string{"llgo", "run", "-v", "."},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clean command",
|
||||||
|
args: []string{"llgo", "clean"},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command",
|
||||||
|
args: []string{"llgo", "cmptest", "."},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command with gen",
|
||||||
|
args: []string{"llgo", "cmptest", "-gen", "."},
|
||||||
|
wantErr: false,
|
||||||
|
setup: func(dir string) error {
|
||||||
|
return os.Remove(filepath.Join(dir, "llgo.expect"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command with args",
|
||||||
|
args: []string{"llgo", "cmptest", ".", "World"},
|
||||||
|
wantErr: true,
|
||||||
|
setup: func(dir string) error {
|
||||||
|
return os.WriteFile(filepath.Join(dir, "llgo.expect"), []byte(`#stdout
|
||||||
|
Hello, World!
|
||||||
|
|
||||||
|
#stderr
|
||||||
|
|
||||||
|
#exit 0
|
||||||
|
`), 0644)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command with different stderr",
|
||||||
|
args: []string{"llgo", "cmptest", ".", "stderr"},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command with different exit code",
|
||||||
|
args: []string{"llgo", "cmptest", ".", "exit"},
|
||||||
|
wantErr: true,
|
||||||
|
setup: func(dir string) error {
|
||||||
|
// Create llgo.expect with different exit code
|
||||||
|
return os.WriteFile(filepath.Join(dir, "llgo.expect"), []byte(`#stdout
|
||||||
|
Hello, LLGO!
|
||||||
|
|
||||||
|
#stderr
|
||||||
|
|
||||||
|
#exit 1
|
||||||
|
`), 0644)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command without llgo.expect to compare with go run",
|
||||||
|
args: []string{"llgo", "cmptest", "."},
|
||||||
|
wantErr: false,
|
||||||
|
setup: func(dir string) error {
|
||||||
|
return os.Remove(filepath.Join(dir, "llgo.expect"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmptest command with different go run output",
|
||||||
|
args: []string{"llgo", "cmptest", "."},
|
||||||
|
wantErr: true,
|
||||||
|
setup: func(dir string) error {
|
||||||
|
// Remove llgo.expect file
|
||||||
|
if err := os.Remove(filepath.Join(dir, "llgo.expect")); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create main_llgo.go for llgo
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "main_llgo.go"), []byte(`//go:build llgo
|
||||||
|
// +build llgo
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, LLGO!")
|
||||||
|
}
|
||||||
|
`), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create main_go.go for go
|
||||||
|
return os.WriteFile(filepath.Join(dir, "main.go"), []byte(`//go:build !llgo
|
||||||
|
// +build !llgo
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, Go!")
|
||||||
|
}
|
||||||
|
`), 0644)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save original args and flags
|
|
||||||
oldArgs := os.Args
|
|
||||||
oldFlagCommandLine := flag.CommandLine
|
|
||||||
oldStdout := os.Stdout
|
|
||||||
oldStderr := os.Stderr
|
|
||||||
defer func() {
|
|
||||||
os.Args = oldArgs
|
|
||||||
flag.CommandLine = oldFlagCommandLine
|
|
||||||
os.Stdout = oldStdout
|
|
||||||
os.Stderr = oldStderr
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Reset flag.CommandLine for each test
|
// Create a new test directory for each test case
|
||||||
flag.CommandLine = flag.NewFlagSet("llgo", flag.ContinueOnError)
|
tmpDir := setupTestProject(t)
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
// Setup command arguments
|
// Change to test project directory
|
||||||
args := append([]string{"llgo", tt.cmd.Name()}, tt.args...)
|
if err := os.Chdir(tmpDir); err != nil {
|
||||||
os.Args = args
|
t.Fatalf("Failed to change directory: %v", err)
|
||||||
|
|
||||||
// Capture output
|
|
||||||
outR, outW, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create stdout pipe: %v", err)
|
|
||||||
}
|
|
||||||
errR, errW, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create stderr pipe: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Stdout = outW
|
if tt.setup != nil {
|
||||||
os.Stderr = errW
|
if err := tt.setup(tmpDir); err != nil {
|
||||||
|
t.Fatalf("Failed to setup test: %v", err)
|
||||||
// Run command
|
|
||||||
done := make(chan struct{})
|
|
||||||
var outBuf, errBuf bytes.Buffer
|
|
||||||
go func() {
|
|
||||||
_, _ = io.Copy(&outBuf, outR)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
_, _ = io.Copy(&errBuf, errR)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
panicked := false
|
|
||||||
func() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
panicked = true
|
|
||||||
t.Logf("%s: Command panicked: %v", tt.name, r)
|
|
||||||
}
|
|
||||||
outW.Close()
|
|
||||||
errW.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
base.CmdName = tt.cmd.Name()
|
|
||||||
|
|
||||||
if !tt.cmd.Runnable() {
|
|
||||||
t.Fatalf("%s: Command is not runnable", tt.name)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Print current working directory and files for debugging
|
mockable.EnableMock()
|
||||||
if cwd, err := os.Getwd(); err == nil {
|
defer func() {
|
||||||
t.Logf("%s: Current working directory: %s", tt.name, cwd)
|
if r := recover(); r != nil {
|
||||||
if files, err := os.ReadDir("."); err == nil {
|
if r != "exit" {
|
||||||
t.Log("Files in current directory:")
|
if !tt.wantErr {
|
||||||
for _, f := range files {
|
t.Errorf("unexpected panic: %v", r)
|
||||||
t.Logf(" %s", f.Name())
|
}
|
||||||
|
} else {
|
||||||
|
exitCode := mockable.ExitCode()
|
||||||
|
if (exitCode != 0) != tt.wantErr {
|
||||||
|
t.Errorf("got exit code %d, wantErr %v", exitCode, tt.wantErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the command
|
|
||||||
tt.cmd.Run(tt.cmd, tt.args)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
<-done
|
os.Args = tt.args
|
||||||
<-done
|
main()
|
||||||
|
|
||||||
// Check output
|
// For build/install commands, check if binary was created
|
||||||
outStr := outBuf.String()
|
if strings.HasPrefix(tt.name, "build") || strings.HasPrefix(tt.name, "install") {
|
||||||
errStr := errBuf.String()
|
binName := "testproject"
|
||||||
|
var binPath string
|
||||||
if outStr == "" && errStr == "" && !panicked {
|
if strings.HasPrefix(tt.name, "install") {
|
||||||
t.Logf("%s: Command completed with no output", tt.name)
|
// For install command, binary should be in GOBIN or GOPATH/bin
|
||||||
} else {
|
gobin := os.Getenv("GOBIN")
|
||||||
if outStr != "" {
|
if gobin == "" {
|
||||||
t.Logf("%s stdout:\n%s", tt.name, outStr)
|
gopath := os.Getenv("GOPATH")
|
||||||
}
|
if gopath == "" {
|
||||||
if errStr != "" {
|
gopath = filepath.Join(os.Getenv("HOME"), "go")
|
||||||
t.Logf("%s stderr:\n%s", tt.name, errStr)
|
}
|
||||||
}
|
gobin = filepath.Join(gopath, "bin")
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the command succeeded
|
|
||||||
if !tt.wantErr {
|
|
||||||
// For build/install commands, check if binary was created
|
|
||||||
if tt.cmd == build.Cmd || tt.cmd == install.Cmd {
|
|
||||||
binName := "testproject"
|
|
||||||
if _, err := os.Stat(binName); os.IsNotExist(err) {
|
|
||||||
t.Logf("%s: Binary %s was not created", tt.name, binName)
|
|
||||||
}
|
}
|
||||||
|
binPath = filepath.Join(gobin, binName)
|
||||||
|
} else {
|
||||||
|
// For build command, binary should be in current directory
|
||||||
|
binPath = filepath.Join(tmpDir, binName)
|
||||||
}
|
}
|
||||||
|
if _, err := os.Stat(binPath); os.IsNotExist(err) {
|
||||||
// For run command, check if output contains expected string
|
t.Errorf("Binary %s was not created at %s", binName, binPath)
|
||||||
if tt.cmd == run.Cmd {
|
|
||||||
if !strings.Contains(outStr, "Hello, LLGO!") {
|
|
||||||
t.Logf("%s: Expected output to contain 'Hello, LLGO!', got:\n%s", tt.name, outStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for common error indicators, but don't fail the test
|
|
||||||
if strings.Contains(errStr, "error:") || strings.Contains(errStr, "failed") {
|
|
||||||
// Ignore LLVM reexported library warning
|
|
||||||
if !strings.Contains(errStr, "ld: warning: reexported library") {
|
|
||||||
t.Logf("%s: Command produced error output:\n%s", tt.name, errStr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -223,119 +274,95 @@ func TestProjectCommands(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCommandHandling(t *testing.T) {
|
func TestCommandHandling(t *testing.T) {
|
||||||
// Save original args and flags
|
|
||||||
oldArgs := os.Args
|
|
||||||
oldFlagCommandLine := flag.CommandLine
|
|
||||||
defer func() {
|
|
||||||
os.Args = oldArgs
|
|
||||||
flag.CommandLine = oldFlagCommandLine
|
|
||||||
}()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
wantErr bool
|
|
||||||
commands []*base.Command
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "version command",
|
|
||||||
args: []string{"llgo", "version"},
|
|
||||||
wantErr: false,
|
|
||||||
commands: []*base.Command{
|
|
||||||
version.Cmd,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "build command",
|
|
||||||
args: []string{"llgo", "build"},
|
|
||||||
wantErr: false,
|
|
||||||
commands: []*base.Command{
|
|
||||||
build.Cmd,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown command",
|
|
||||||
args: []string{"llgo", "unknowncommand"},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "help command",
|
|
||||||
args: []string{"llgo", "help"},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// Reset flag.CommandLine for each test
|
|
||||||
flag.CommandLine = flag.NewFlagSet(tt.args[0], flag.ExitOnError)
|
|
||||||
os.Args = tt.args
|
|
||||||
|
|
||||||
if tt.commands != nil {
|
|
||||||
base.Llgo.Commands = tt.commands
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture panic that would normally exit
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if !tt.wantErr {
|
|
||||||
t.Errorf("unexpected panic: %v", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
if len(tt.args) > 1 {
|
|
||||||
base.CmdName = tt.args[1]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHelpCommand(t *testing.T) {
|
|
||||||
oldArgs := os.Args
|
|
||||||
oldFlagCommandLine := flag.CommandLine
|
|
||||||
defer func() {
|
|
||||||
os.Args = oldArgs
|
|
||||||
flag.CommandLine = oldFlagCommandLine
|
|
||||||
}()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "help without subcommand",
|
name: "version command",
|
||||||
|
args: []string{"llgo", "version"},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help command",
|
||||||
args: []string{"llgo", "help"},
|
args: []string{"llgo", "help"},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "help with subcommand",
|
name: "invalid command",
|
||||||
args: []string{"llgo", "help", "build"},
|
args: []string{"llgo", "invalid"},
|
||||||
wantErr: false,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
flag.CommandLine = flag.NewFlagSet(tt.args[0], flag.ExitOnError)
|
|
||||||
os.Args = tt.args
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
if !tt.wantErr {
|
if r != "exit" {
|
||||||
t.Errorf("unexpected panic: %v", r)
|
t.Errorf("unexpected panic: %v", r)
|
||||||
}
|
}
|
||||||
|
exitCode := mockable.ExitCode()
|
||||||
|
if (exitCode != 0) != tt.wantErr {
|
||||||
|
t.Errorf("got exit code %d, wantErr %v", exitCode, tt.wantErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
flag.Parse()
|
os.Args = tt.args
|
||||||
args := flag.Args()
|
main()
|
||||||
if len(args) > 0 && args[0] == "help" {
|
})
|
||||||
help.Help(&buf, args[1:])
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHelpCommand(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "help build",
|
||||||
|
args: []string{"llgo", "help", "build"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help install",
|
||||||
|
args: []string{"llgo", "help", "install"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help run",
|
||||||
|
args: []string{"llgo", "help", "run"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help version",
|
||||||
|
args: []string{"llgo", "help", "version"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help clean",
|
||||||
|
args: []string{"llgo", "help", "clean"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help cmptest",
|
||||||
|
args: []string{"llgo", "help", "cmptest"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if r != "exit" {
|
||||||
|
t.Errorf("unexpected panic: %v", r)
|
||||||
|
}
|
||||||
|
exitCode := mockable.ExitCode()
|
||||||
|
if exitCode != 0 {
|
||||||
|
t.Errorf("got exit code %d, want 0", exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
os.Args = tt.args
|
||||||
|
main()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import (
|
|||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cl"
|
"github.com/goplus/llgo/compiler/cl"
|
||||||
"github.com/goplus/llgo/compiler/internal/env"
|
"github.com/goplus/llgo/compiler/internal/env"
|
||||||
|
"github.com/goplus/llgo/compiler/internal/mockable"
|
||||||
"github.com/goplus/llgo/compiler/internal/packages"
|
"github.com/goplus/llgo/compiler/internal/packages"
|
||||||
"github.com/goplus/llgo/compiler/internal/typepatch"
|
"github.com/goplus/llgo/compiler/internal/typepatch"
|
||||||
"github.com/goplus/llgo/compiler/ssa/abi"
|
"github.com/goplus/llgo/compiler/ssa/abi"
|
||||||
@@ -221,15 +222,11 @@ func Do(args []string, conf *Config) ([]Package, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mode != ModeBuild {
|
if mode != ModeBuild {
|
||||||
nErr := 0
|
|
||||||
for _, pkg := range initial {
|
for _, pkg := range initial {
|
||||||
if pkg.Name == "main" {
|
if pkg.Name == "main" {
|
||||||
nErr += linkMainPkg(ctx, pkg, pkgs, linkArgs, conf, mode, verbose)
|
linkMainPkg(ctx, pkg, pkgs, linkArgs, conf, mode, verbose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if nErr > 0 {
|
|
||||||
os.Exit(nErr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return dpkg, nil
|
return dpkg, nil
|
||||||
}
|
}
|
||||||
@@ -290,7 +287,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
|
|||||||
fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
|
fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
|
||||||
}
|
}
|
||||||
if len(errPkgs) > 0 {
|
if len(errPkgs) > 0 {
|
||||||
os.Exit(1)
|
mockable.Exit(1)
|
||||||
}
|
}
|
||||||
built := ctx.built
|
built := ctx.built
|
||||||
for _, aPkg := range pkgs {
|
for _, aPkg := range pkgs {
|
||||||
@@ -372,7 +369,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs []string, conf *Config, mode Mode, verbose bool) (nErr int) {
|
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs []string, conf *Config, mode Mode, verbose bool) {
|
||||||
pkgPath := pkg.PkgPath
|
pkgPath := pkg.PkgPath
|
||||||
name := path.Base(pkgPath)
|
name := path.Base(pkgPath)
|
||||||
app := conf.OutFile
|
app := conf.OutFile
|
||||||
@@ -458,11 +455,6 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs
|
|||||||
if verbose || mode != ModeRun {
|
if verbose || mode != ModeRun {
|
||||||
fmt.Fprintln(os.Stderr, "#", pkgPath)
|
fmt.Fprintln(os.Stderr, "#", pkgPath)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
nErr = 1
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// add rpath and find libs
|
// add rpath and find libs
|
||||||
exargs := make([]string, 0, ctx.nLibdir<<1)
|
exargs := make([]string, 0, ctx.nLibdir<<1)
|
||||||
@@ -506,12 +498,11 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Run()
|
cmd.Run()
|
||||||
if s := cmd.ProcessState; s != nil {
|
if s := cmd.ProcessState; s != nil {
|
||||||
os.Exit(s.ExitCode())
|
mockable.Exit(s.ExitCode())
|
||||||
}
|
}
|
||||||
case ModeCmpTest:
|
case ModeCmpTest:
|
||||||
cmpTest(filepath.Dir(pkg.GoFiles[0]), pkgPath, app, conf.GenExpect, conf.RunArgs)
|
cmpTest(filepath.Dir(pkg.GoFiles[0]), pkgPath, app, conf.GenExpect, conf.RunArgs)
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string, err error) {
|
func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string, err error) {
|
||||||
|
|||||||
51
compiler/internal/env/env.go
vendored
51
compiler/internal/env/env.go
vendored
@@ -2,9 +2,11 @@ package env
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@ const (
|
|||||||
LLGoCompilerPkg = "github.com/goplus/llgo"
|
LLGoCompilerPkg = "github.com/goplus/llgo"
|
||||||
LLGoRuntimePkgName = "runtime"
|
LLGoRuntimePkgName = "runtime"
|
||||||
LLGoRuntimePkg = LLGoCompilerPkg + "/" + LLGoRuntimePkgName
|
LLGoRuntimePkg = LLGoCompilerPkg + "/" + LLGoRuntimePkgName
|
||||||
|
envFileName = "/compiler/internal/env/env.go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GOROOT() string {
|
func GOROOT() string {
|
||||||
@@ -38,8 +41,12 @@ func LLGoRuntimeDir() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func LLGoROOT() string {
|
func LLGoROOT() string {
|
||||||
if root, ok := isLLGoRoot(os.Getenv("LLGO_ROOT")); ok {
|
llgoRootEnv := os.Getenv("LLGO_ROOT")
|
||||||
return root
|
if llgoRootEnv != "" {
|
||||||
|
if root, ok := isLLGoRoot(llgoRootEnv); ok {
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: LLGO_ROOT is not a valid LLGO root: %s\n", llgoRootEnv)
|
||||||
}
|
}
|
||||||
// Get executable path
|
// Get executable path
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
@@ -53,13 +60,22 @@ func LLGoROOT() string {
|
|||||||
}
|
}
|
||||||
// Check if parent directory is bin
|
// Check if parent directory is bin
|
||||||
dir := filepath.Dir(exe)
|
dir := filepath.Dir(exe)
|
||||||
if filepath.Base(dir) != "bin" {
|
if filepath.Base(dir) == "bin" {
|
||||||
return ""
|
// Get parent directory of bin
|
||||||
|
root := filepath.Dir(dir)
|
||||||
|
if root, ok := isLLGoRoot(root); ok {
|
||||||
|
return root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Get parent directory of bin
|
if Devel() {
|
||||||
root := filepath.Dir(dir)
|
root, err := getRuntimePkgDirByCaller()
|
||||||
if root, ok := isLLGoRoot(root); ok {
|
if err != nil {
|
||||||
return root
|
return ""
|
||||||
|
}
|
||||||
|
if root, ok := isLLGoRoot(root); ok {
|
||||||
|
fmt.Fprintln(os.Stderr, "WARNING: Using LLGO root for devel: "+root)
|
||||||
|
return root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -83,3 +99,22 @@ func isLLGoRoot(root string) (string, bool) {
|
|||||||
}
|
}
|
||||||
return root, true
|
return root, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRuntimePkgDirByCaller() (string, error) {
|
||||||
|
_, file, _, ok := runtime.Caller(0)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("cannot get caller")
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(file, envFileName) {
|
||||||
|
return "", fmt.Errorf("wrong caller")
|
||||||
|
}
|
||||||
|
// check file exists
|
||||||
|
if _, err := os.Stat(file); os.IsNotExist(err) {
|
||||||
|
return "", fmt.Errorf("file %s not exists", file)
|
||||||
|
}
|
||||||
|
modPath := strings.TrimSuffix(file, envFileName)
|
||||||
|
if st, err := os.Stat(modPath); os.IsNotExist(err) || !st.IsDir() {
|
||||||
|
return "", fmt.Errorf("not llgo compiler root: %s", modPath)
|
||||||
|
}
|
||||||
|
return modPath, nil
|
||||||
|
}
|
||||||
|
|||||||
33
compiler/internal/env/env_test.go
vendored
33
compiler/internal/env/env_test.go
vendored
@@ -58,8 +58,28 @@ func TestLLGoRuntimeDir(t *testing.T) {
|
|||||||
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
||||||
|
|
||||||
os.Setenv("LLGO_ROOT", "/nonexistent/path")
|
os.Setenv("LLGO_ROOT", "/nonexistent/path")
|
||||||
if got := LLGoRuntimeDir(); got != "" {
|
wd, err := os.Getwd()
|
||||||
t.Errorf("LLGoRuntimeDir() = %v, want empty string", got)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
runtimeDir := filepath.Join(wd, "../../../runtime")
|
||||||
|
if got := LLGoRuntimeDir(); got != runtimeDir {
|
||||||
|
t.Errorf("LLGoRuntimeDir() = %v, want %v", got, runtimeDir)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("devel runtime dir", func(t *testing.T) {
|
||||||
|
origLLGoRoot := os.Getenv("LLGO_ROOT")
|
||||||
|
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
||||||
|
|
||||||
|
os.Setenv("LLGO_ROOT", "")
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
runtimeDir := filepath.Join(wd, "../../../runtime")
|
||||||
|
if got := LLGoRuntimeDir(); got != runtimeDir {
|
||||||
|
t.Errorf("LLGoRuntimeDir() = %v, want %v", got, runtimeDir)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -90,8 +110,13 @@ func TestLLGoROOT(t *testing.T) {
|
|||||||
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
||||||
|
|
||||||
os.Setenv("LLGO_ROOT", "/nonexistent/path")
|
os.Setenv("LLGO_ROOT", "/nonexistent/path")
|
||||||
if got := LLGoROOT(); got != "" {
|
wd, err := os.Getwd()
|
||||||
t.Errorf("LLGoROOT() = %v, want empty string", got)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rootDir := filepath.Join(wd, "../../..")
|
||||||
|
if got := LLGoROOT(); got != rootDir {
|
||||||
|
t.Errorf("LLGoROOT() = %v, want %v", got, rootDir)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -16,20 +16,32 @@
|
|||||||
|
|
||||||
package env
|
package env
|
||||||
|
|
||||||
import "runtime/debug"
|
import (
|
||||||
|
"runtime/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
devel = "(devel)"
|
||||||
|
)
|
||||||
|
|
||||||
// buildVersion is the LLGo tree's version string at build time. It should be
|
// buildVersion is the LLGo tree's version string at build time. It should be
|
||||||
// set by the linker.
|
// set by the linker.
|
||||||
var buildVersion string
|
var buildVersion string
|
||||||
|
|
||||||
// Version returns the version of the running LLGo binary.
|
// Version returns the version of the running LLGo binary.
|
||||||
|
//
|
||||||
|
//export LLGoVersion
|
||||||
func Version() string {
|
func Version() string {
|
||||||
if buildVersion != "" {
|
if buildVersion != "" {
|
||||||
return buildVersion
|
return buildVersion
|
||||||
}
|
}
|
||||||
info, ok := debug.ReadBuildInfo()
|
info, ok := debug.ReadBuildInfo()
|
||||||
if ok {
|
if ok && info.Main.Version != "" {
|
||||||
return info.Main.Version
|
return info.Main.Version
|
||||||
}
|
}
|
||||||
return "(devel)"
|
return devel
|
||||||
|
}
|
||||||
|
|
||||||
|
func Devel() bool {
|
||||||
|
return Version() == devel
|
||||||
}
|
}
|
||||||
29
compiler/internal/mockable/mockable.go
Normal file
29
compiler/internal/mockable/mockable.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package mockable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
exitFunc = os.Exit
|
||||||
|
exitCode int
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnableMock enables mocking of os.Exit
|
||||||
|
func EnableMock() {
|
||||||
|
exitCode = 0
|
||||||
|
exitFunc = func(code int) {
|
||||||
|
exitCode = code
|
||||||
|
panic("exit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit calls the current exit function
|
||||||
|
func Exit(code int) {
|
||||||
|
exitFunc(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExitCode returns the last exit code from a mocked Exit call
|
||||||
|
func ExitCode() int {
|
||||||
|
return exitCode
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ package ssa_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goplus/llgo/compiler/cl/cltest"
|
"github.com/goplus/llgo/compiler/cl/cltest"
|
||||||
@@ -26,6 +29,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
devNull, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
|
||||||
|
os.Stderr = devNull
|
||||||
|
os.Stdout = devNull
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
ssa.SetDebug(ssa.DbgFlagAll)
|
ssa.SetDebug(ssa.DbgFlagAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user