diff --git a/README.md b/README.md index f0dc212a..31596e4f 100644 --- a/README.md +++ b/README.md @@ -566,3 +566,84 @@ go tool pprof ./testdata/cpu.out For more information, see [Profiling Go Programs](https://blog.golang.org/profiling-go-programs). + +# Prototype `c4go make` + +Run `c4go make` instead of `make`. +Internally replace replace compiler - `make CC="c4go compiler"`. +If you run for example `c4go make MALLOC=libc`, then internally run +`make MALLOC=libc CC="c4go compiler"`. +At the end of `make` run specific step for transpilation and assembly Go files. + +Run: +```sh +make +``` +Output: +```sh +cc -c -Wall -O2 vi.c +cc -c -Wall -O2 ex.c +cc -c -Wall -O2 lbuf.c +cc -c -Wall -O2 mot.c +cc -c -Wall -O2 sbuf.c +cc -c -Wall -O2 ren.c +cc -c -Wall -O2 dir.c +cc -c -Wall -O2 syn.c +cc -c -Wall -O2 reg.c +cc -c -Wall -O2 led.c +cc -c -Wall -O2 uc.c +cc -c -Wall -O2 term.c +cc -c -Wall -O2 rset.c +cc -c -Wall -O2 regex.c +cc -c -Wall -O2 cmd.c +cc -c -Wall -O2 conf.c +cc -o vi vi.o ex.o lbuf.o mot.o sbuf.o ren.o dir.o syn.o reg.o led.o uc.o term.o rset.o regex.o cmd.o conf.o +``` + +Transpile: +```sh +c4go make -CC=clang +``` +Output: +```sh +c4go compiler -c -Wall -O2 vi.c +c4go compiler -c -Wall -O2 ex.c +c4go compiler -c -Wall -O2 lbuf.c +c4go compiler -c -Wall -O2 mot.c +c4go compiler -c -Wall -O2 sbuf.c +c4go compiler -c -Wall -O2 ren.c +c4go compiler -c -Wall -O2 dir.c +c4go compiler -c -Wall -O2 syn.c +c4go compiler -c -Wall -O2 reg.c +c4go compiler -c -Wall -O2 led.c +c4go compiler -c -Wall -O2 uc.c +c4go compiler -c -Wall -O2 term.c +c4go compiler -c -Wall -O2 rset.c +c4go compiler -c -Wall -O2 regex.c +c4go compiler -c -Wall -O2 cmd.c +c4go compiler -c -Wall -O2 conf.c +c4go compiler -o vi vi.o ex.o lbuf.o mot.o sbuf.o ren.o dir.o syn.o reg.o led.o uc.o term.o rset.o regex.o cmd.o conf.o +``` + +**Internally**. +After run `c4go make -CC=clang MALLOC=glibc` created configuration file `c4go_make.conf` , +if not exist or remove present configuration, if exist, +with all flags, in that case: +``` +clang +MALLOC=glibc ...other flags... +``` +Running internally `make CC="c4go compiler" MALLOC=glibc ...other flags...`. +`Make` run `c4go compiler MALLOC=glibc ...other flags... regex.c` + +Application `c4go compiler`: +* read configuration file for using right C compiler, if configuration file + is not exist used `clang` +* write all flags in file `c4go_compiler.conf` +* run `clang MALLOC=glibc ...other flags... regex.c` +* transpile in according to flags + +After end of `make` script, write on screen all steps of making. + + + diff --git a/ast/ast.go b/ast/ast.go index 44c6c4b4..5b1f0ffa 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -64,6 +64,8 @@ func Parse(fullline string) (returnNode Node, err error) { return parseAccessSpecDecl(line), nil case "AlignedAttr": return parseAlignedAttr(line), nil + case "GNUInlineAttr": + return parseGNUInlineAttr(line), nil case "AnnotateAttr": return parseAnnotateAttr(line), nil case "AllocSizeAttr": diff --git a/ast/gnu_inline_attr.go b/ast/gnu_inline_attr.go new file mode 100644 index 00000000..1048a2f4 --- /dev/null +++ b/ast/gnu_inline_attr.go @@ -0,0 +1,44 @@ +package ast + +// GNUInlineAttr +type GNUInlineAttr struct { + Addr Address + Pos Position + ChildNodes []Node +} + +func parseGNUInlineAttr(line string) *GNUInlineAttr { + groups := groupsFromRegex( + `<(?P.*)>`, + line, + ) + + return &GNUInlineAttr{ + Addr: ParseAddress(groups["address"]), + Pos: NewPositionFromString(groups["position"]), + ChildNodes: []Node{}, + } +} + +// AddChild adds a new child node. Child nodes can then be accessed with the +// Children attribute. +func (n *GNUInlineAttr) AddChild(node Node) { + n.ChildNodes = append(n.ChildNodes, node) +} + +// Address returns the numeric address of the node. See the documentation for +// the Address type for more information. +func (n *GNUInlineAttr) Address() Address { + return n.Addr +} + +// Children returns the child nodes. If this node does not have any children or +// this node does not support children it will always return an empty slice. +func (n *GNUInlineAttr) Children() []Node { + return n.ChildNodes +} + +// Position returns the position in the original source code. +func (n *GNUInlineAttr) Position() Position { + return n.Pos +} diff --git a/ast/gnu_inline_attr_test.go b/ast/gnu_inline_attr_test.go new file mode 100644 index 00000000..926188f7 --- /dev/null +++ b/ast/gnu_inline_attr_test.go @@ -0,0 +1,17 @@ +package ast + +import ( + "testing" +) + +func TestGNUInlineAttr(t *testing.T) { + nodes := map[string]Node{ + `0xb8a12b58 `: &GNUInlineAttr{ + Addr: 0xb8a12b58, + Pos: NewPositionFromString("col:62, col:92"), + ChildNodes: []Node{}, + }, + } + + runNodeTests(t, nodes) +} diff --git a/ast/position.go b/ast/position.go index b429f792..8fe1d535 100644 --- a/ast/position.go +++ b/ast/position.go @@ -236,6 +236,8 @@ func setPosition(node Node, position Position) { n.Pos = position case *AlignedAttr: n.Pos = position + case *GNUInlineAttr: + n.Pos = position case *AnnotateAttr: n.Pos = position case *AllocSizeAttr: diff --git a/main.go b/main.go index 4e5e0917..6e0f9d89 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ package main import ( + "bytes" "flag" "fmt" "io" @@ -35,6 +36,12 @@ import ( var stderr io.Writer = os.Stderr var astout io.Writer = os.Stdout +// filenames of configuration files +const ( + configFilename string = "c4go_make.conf" + configFilenameCompiler = "c4go_compiler.conf" +) + // ProgramArgs defines the options available when processing the program. There // is no constructor since the zeroed out values are the appropriate defaults - // you need only set the options you need. @@ -556,6 +563,7 @@ func (i *inputDataFlags) Set(value string) error { func main() { code := runCommand() if code != 0 { + fmt.Fprintf(stderr, "\nExit code : %v\n", code) os.Exit(code) } } @@ -597,6 +605,14 @@ func runCommand() int { "p", "debug.", "prefix of output C filename with addition debug informations") debugHelpFlag = debugCommand.Bool( "h", false, "print help information") + + makeCommand = flag.NewFlagSet( + "make", flag.ContinueOnError) + makeCompilerFlag = makeCommand.String( + "CC", "clang", "default compiler") + + compilerCommand = flag.NewFlagSet( + "compiler", flag.ContinueOnError) ) var clangFlags inputDataFlags transpileCommand.Var(&clangFlags, @@ -617,6 +633,8 @@ func runCommand() int { usage += " transpile\ttranspile an input C source file or files to Go\n" usage += " ast\t\tprint AST before translated Go code\n" usage += " debug\t\tadd debug information in C source\n" + usage += " make\t\trun script make of C project for automatically transpilation\n" + usage += " compiler\ttransfer to C compiler. Do not use this alone. Use command `transpile` or `make`\n" usage += " version\tprint version of c4go\n" usage += "\n" fmt.Fprintf(stderr, usage, os.Args[0]) @@ -627,6 +645,8 @@ func runCommand() int { transpileCommand.SetOutput(stderr) astCommand.SetOutput(stderr) debugCommand.SetOutput(stderr) + makeCommand.SetOutput(stderr) + compilerCommand.SetOutput(stderr) flag.Parse() @@ -712,6 +732,112 @@ func runCommand() int { args.clangFlags = clangFlags args.cppCode = *debugCppFlag + case "make": + // remove compiler configuration file, if exist + if err := ioutil.WriteFile(configFilenameCompiler, []byte{}, 0644); err != nil { + fmt.Fprintf(stderr, "%s", err) + return 88 + } + + // write configuration file + if err := ioutil.WriteFile(configFilename, + []byte(fmt.Sprintf("%s\n%s", *makeCompilerFlag, strings.Join(makeCommand.Args(), " "))), + 0644); err != nil { + fmt.Fprintf(stderr, "%s", err) + return 87 + } + + // run make + arguments := append([]string{fmt.Sprintf("CC=%s compiler", os.Args[0])}, + makeCommand.Args()...) + fmt.Fprintf(os.Stdout, "c4go : Run make %v\n", arguments) + var cmd *exec.Cmd + cmd = exec.Command("make", arguments...) + var stderrE bytes.Buffer + cmd.Stdout = os.Stdout + cmd.Stderr = &stderrE + err := cmd.Run() + if err != nil { + fmt.Fprintf(stderr, "%s\n%v", stderrE.String(), err) + return 89 + } + + // transpile + fmt.Fprintf(os.Stdout, "c4go : Make : run transpilation\n") + + dat, err := ioutil.ReadFile(configFilenameCompiler) + if err != nil { + fmt.Fprintf(stderr, "%v", err) + return 84 + } + parts := strings.Split(strings.Replace(string(dat), "\n", " ", -1), " ") + + args = convertArg(parts) + + fmt.Fprintf(os.Stdout, "c4go : Make : Transpilation. inputFiles: %s\n", args.inputFiles) + fmt.Fprintf(os.Stdout, "c4go : Make : Transpilation. outputFile: %s\n", args.outputFile) + fmt.Fprintf(os.Stdout, "c4go : Make : Transpilation. clangFlags: %s\n", args.clangFlags) + + if len(args.inputFiles) == 0 { + return 0 + } + + fmt.Fprintf(os.Stdout, "c4go : End of make ...\n") + + case "compiler": + var compilerName string = "clang" + if _, err := os.Stat(configFilename); os.IsExist(err) { + if dat, err := ioutil.ReadFile(configFilename); err == nil { + if lines := bytes.Split(dat, []byte{'\n'}); len(lines) > 0 { + compilerName = string(lines[0]) + } + } + } + + fmt.Fprintf(os.Stdout, "c4go : Run compiler: %s\n", compilerName) + + // If the file doesn't exist, create it, or append to the file + f, err := os.OpenFile(configFilenameCompiler, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Fprintf(stderr, "%s", err) + return 100 + } + arguments := strings.Join(os.Args[2:], " ") + "\n" + if _, err := f.Write([]byte(arguments)); err != nil { + fmt.Fprintf(stderr, "%s", err) + return 101 + } + if err := f.Close(); err != nil { + fmt.Fprintf(stderr, "%s", err) + return 102 + } + fmt.Fprintf(os.Stdout, "c4go : Compiler use flags: %v\n", os.Args) + + // run C compiler + fmt.Fprintf(os.Stdout, "c4go : Compiler: run C compiler: %v %v\n", compilerName, os.Args[2:]) + var cmd *exec.Cmd + cmd = exec.Command(compilerName, os.Args[2:]...) + var stderrE bytes.Buffer + cmd.Stdout = os.Stdout + cmd.Stderr = &stderrE + if err := cmd.Run(); err != nil { + fmt.Fprintf(stderr, "%s\n%v", stderrE.String(), err) + return 103 + } + + // transpile + fmt.Fprintf(os.Stdout, "c4go : Compiler: run transpilation\n") + + args = convertArg(os.Args[2:]) + + fmt.Fprintf(os.Stdout, "c4go : Transpilation. inputFiles: %s\n", args.inputFiles) + fmt.Fprintf(os.Stdout, "c4go : Transpilation. outputFile: %s\n", args.outputFile) + fmt.Fprintf(os.Stdout, "c4go : Transpilation. clangFlags: %s\n", args.clangFlags) + + if len(args.inputFiles) == 0 { + return 0 + } + case "version": fmt.Fprint(stderr, version.Version()) return 0 @@ -728,3 +854,48 @@ func runCommand() int { return 0 } + +func convertArg(ps []string) (args ProgramArgs) { + args.state = StateTranspile + args.outsideStructs = true + args.packageName = "main" + args.verbose = false + args.cppCode = false + + notInclude := []string{ + // optimization + "-O1", "-O2", "-O3", "-O4", + } + + // examples compilerCommand.Args(): + // -c -Wall -O2 conf.c + // -o vi vi.o ex.o lbuf.o mot.o + for i := 0; i < len(ps); i++ { + found := false + for _, ni := range notInclude { + if ps[i] == ni { + found = true + break + } + } + if found { + continue + } + if ps[i] == "-o" && i+1 <= len(ps) && !strings.HasSuffix(ps[i+1], ".o") { + args.outputFile = ps[i+1] + ".go" + i++ + continue + } + if strings.HasSuffix(ps[i], ".c") { + args.inputFiles = append(args.inputFiles, ps[i]) + continue + } + if strings.HasSuffix(ps[i], ".o") { + // ignore object file + continue + } + args.clangFlags = append(args.clangFlags, ps[i]) + } + + return +}