Skip to content

c4go make: create new feature #453

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.



2 changes: 2 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
44 changes: 44 additions & 0 deletions ast/gnu_inline_attr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ast

// GNUInlineAttr
type GNUInlineAttr struct {
Addr Address
Pos Position
ChildNodes []Node
}

func parseGNUInlineAttr(line string) *GNUInlineAttr {
groups := groupsFromRegex(
`<(?P<position>.*)>`,
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
}
17 changes: 17 additions & 0 deletions ast/gnu_inline_attr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ast

import (
"testing"
)

func TestGNUInlineAttr(t *testing.T) {
nodes := map[string]Node{
`0xb8a12b58 <col:62, col:92>`: &GNUInlineAttr{
Addr: 0xb8a12b58,
Pos: NewPositionFromString("col:62, col:92"),
ChildNodes: []Node{},
},
}

runNodeTests(t, nodes)
}
2 changes: 2 additions & 0 deletions ast/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
171 changes: 171 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package main

import (
"bytes"
"flag"
"fmt"
"io"
Expand All @@ -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.
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -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])
Expand All @@ -627,6 +645,8 @@ func runCommand() int {
transpileCommand.SetOutput(stderr)
astCommand.SetOutput(stderr)
debugCommand.SetOutput(stderr)
makeCommand.SetOutput(stderr)
compilerCommand.SetOutput(stderr)

flag.Parse()

Expand Down Expand Up @@ -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
Expand All @@ -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
}