| /* |
| Copyright 2015 Runtime Inc. |
| 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 cli |
| |
| import ( |
| "io/ioutil" |
| "log" |
| "os" |
| "path/filepath" |
| "strings" |
| ) |
| |
| type Compiler struct { |
| ConfigPath string |
| TargetName string |
| BaseIncludes []string |
| ObjPathList map[string]bool |
| LinkerScript string |
| |
| Cflags string |
| Aflags string |
| Lflags string |
| |
| ccPath string |
| asPath string |
| arPath string |
| odPath string |
| osPath string |
| ocPath string |
| ldFlags string |
| ldResolveCircularDeps bool |
| ldMapFile bool |
| } |
| |
| func NewCompiler(ccPath string, cDef string, tName string, includes []string) ( |
| *Compiler, error) { |
| c := &Compiler{ |
| ConfigPath: ccPath, |
| TargetName: tName, |
| BaseIncludes: includes, |
| } |
| |
| log.Printf("[INFO] Loading compiler %s, target %s, def %s", ccPath, tName, cDef) |
| |
| err := c.ReadSettings(cDef) |
| if err != nil { |
| return nil, err |
| } |
| |
| c.ObjPathList = make(map[string]bool) |
| |
| return c, nil |
| } |
| |
| func (c *Compiler) ReadSettings(cDef string) error { |
| v, err := ReadConfig(c.ConfigPath, "compiler") |
| if err != nil { |
| return err |
| } |
| |
| c.ccPath = v.GetString("compiler.path.cc") |
| c.asPath = v.GetString("compiler.path.as") |
| c.arPath = v.GetString("compiler.path.archive") |
| c.odPath = v.GetString("compiler.path.objdump") |
| c.osPath = v.GetString("compiler.path.objsize") |
| c.ocPath = v.GetString("compiler.path.objcopy") |
| |
| cflags := v.GetStringSlice("compiler.flags." + cDef) |
| for _, flag := range cflags { |
| if strings.HasPrefix(flag, "compiler.flags") { |
| c.Cflags += " " + strings.Trim(v.GetString(flag), "\n") |
| } else { |
| c.Cflags += " " + strings.Trim(flag, "\n") |
| } |
| } |
| |
| c.ldFlags = v.GetString("compiler.ld.flags") |
| c.ldResolveCircularDeps = v.GetBool("compiler.ld.resolve_circular_deps") |
| c.ldMapFile = v.GetBool("compiler.ld.mapfile") |
| |
| log.Printf("[INFO] ccPath = %s, arPath = %s, flags = %s", c.ccPath, |
| c.arPath, c.Cflags) |
| |
| return nil |
| } |
| |
| // file type 0 = cc, file type 1 = as |
| func (c *Compiler) CompileFile(file string, compilerType int) error { |
| wd, _ := os.Getwd() |
| objDir := wd + "/obj/" + c.TargetName + "/" |
| |
| if NodeNotExist(objDir) { |
| os.MkdirAll(objDir, 0755) |
| } |
| |
| objFile := strings.TrimSuffix(file, filepath.Ext(file)) + ".o" |
| |
| objPath := objDir + objFile |
| c.ObjPathList[objPath] = true |
| |
| var cmd string |
| |
| switch compilerType { |
| case 0: |
| cmd = c.ccPath |
| case 1: |
| cmd = c.asPath |
| default: |
| return NewNewtError("Unknown compiler type") |
| } |
| |
| cmd += " -c " + "-o " + objPath + " " + file + |
| " " + c.Cflags + " -I" + strings.Join(c.BaseIncludes, " -I") |
| |
| _, err := ShellCommand(cmd) |
| return err |
| } |
| |
| func (c *Compiler) Compile(match string) error { |
| files, _ := filepath.Glob(match) |
| |
| wd, err := os.Getwd() |
| if err != nil { |
| return err |
| } |
| |
| log.Printf("[INFO] Compiling C (%s/%s) %s", wd, match, strings.Join(files, " ")) |
| |
| for _, file := range files { |
| err := c.CompileFile(file, 0) |
| if err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func (c *Compiler) CompileAs(match string) error { |
| files, _ := filepath.Glob(match) |
| |
| wd, err := os.Getwd() |
| if err != nil { |
| return err |
| } |
| |
| log.Printf("[INFO] Compiling assembly (%s/%s) %s", wd, match, strings.Join(files, " ")) |
| |
| for _, file := range files { |
| err := c.CompileFile(file, 1) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func (c *Compiler) RecursiveClean(path string, tName string) error { |
| // Find all the subdirectories of path that contain an "obj/" directory, and |
| // remove that directory either altogether, or just the arch specific directory. |
| dirList, err := ioutil.ReadDir(path) |
| if err != nil { |
| return NewNewtError(err.Error()) |
| } |
| |
| for _, node := range dirList { |
| if node.IsDir() { |
| if node.Name() == "obj" || node.Name() == "bin" { |
| if tName == "" { |
| os.RemoveAll(path + "/" + node.Name() + "/") |
| } else { |
| os.RemoveAll(path + "/" + node.Name() + "/" + tName + "/") |
| } |
| } else { |
| // recurse into the directory. |
| err = c.RecursiveClean(path+"/"+node.Name(), tName) |
| if err != nil { |
| return err |
| } |
| } |
| } |
| } |
| |
| return nil |
| } |
| |
| func (c *Compiler) processEntry(wd string, node os.FileInfo, match string, cType int, |
| ignDirs []string) error { |
| // check to see if we ignore this element |
| for _, entry := range ignDirs { |
| if entry == node.Name() { |
| return nil |
| } |
| } |
| |
| // if not, recurse into the directory |
| os.Chdir(wd + "/" + node.Name()) |
| return c.RecursiveCompile(match, cType, ignDirs) |
| } |
| |
| func (c *Compiler) RecursiveCompile(match string, cType int, ignDirs []string) error { |
| // Get a list of files in the current directory, and if they are a directory, |
| // and that directory is not in the ignDirs variable, then recurse into that |
| // directory and compile the files in there |
| wd, err := os.Getwd() |
| if err != nil { |
| return NewNewtError(err.Error()) |
| } |
| |
| dirList, err := ioutil.ReadDir(wd) |
| if err != nil { |
| return NewNewtError(err.Error()) |
| } |
| |
| for _, node := range dirList { |
| if node.IsDir() { |
| err = c.processEntry(wd, node, match, cType, ignDirs) |
| if err != nil { |
| return err |
| } |
| } |
| } |
| |
| os.Chdir(wd) |
| |
| switch cType { |
| case 0: |
| return c.Compile(match) |
| case 1: |
| return c.CompileAs(match) |
| default: |
| return NewNewtError("Wrong compiler type specified to RecursiveCompile") |
| } |
| } |
| |
| func (c *Compiler) getObjFiles(baseObjFiles string) string { |
| objList := baseObjFiles |
| for objName, _ := range c.ObjPathList { |
| objList += " " + objName |
| } |
| return objList |
| } |
| |
| func (c *Compiler) CompileBinary(dstFile string, options map[string]bool, |
| objFiles string) error { |
| objList := c.getObjFiles(objFiles) |
| |
| log.Printf("[INFO] Compiling Binary %s with object files %s", dstFile, |
| objList) |
| |
| cmd := c.ccPath + " -o " + dstFile + " " + c.ldFlags + " " + c.Cflags |
| if c.ldResolveCircularDeps { |
| cmd += " -Wl,--start-group -lc " + objList + " -Wl,--end-group " |
| } else { |
| cmd += " " + objList |
| } |
| |
| if c.LinkerScript != "" { |
| cmd += " -T " + c.LinkerScript |
| } |
| if checkBoolMap(options, "mapFile") { |
| cmd += " -Wl,-Map=" + dstFile + ".map" |
| } |
| |
| _, err := ShellCommand(cmd) |
| if err != nil { |
| return err |
| } |
| |
| if checkBoolMap(options, "listFile") { |
| listFile := dstFile + ".lst" |
| // if list file exists, remove it |
| if NodeExist(listFile) { |
| if err := os.RemoveAll(listFile); err != nil { |
| return err |
| } |
| } |
| |
| cmd = c.odPath + " -wxdS " + dstFile + " >> " + listFile |
| _, err := ShellCommand(cmd) |
| if err != nil { |
| // XXX: gobjdump appears to always crash. Until we get that sorted |
| // out, don't fail the link process if lst generation fails. |
| return nil |
| } |
| |
| sects := []string{".text", ".rodata", ".data"} |
| for _, sect := range sects { |
| cmd = c.odPath + " -s -j " + sect + " " + dstFile + " >> " + listFile |
| ShellCommand(cmd) |
| } |
| |
| cmd = c.osPath + " " + dstFile + " >> " + listFile |
| } |
| |
| if checkBoolMap(options, "binFile") { |
| binFile := dstFile + ".bin" |
| cmd = c.ocPath + " -R .bss -R .bss.core -R .bss.core.nz -O binary " + |
| dstFile + " " + binFile |
| _, err := ShellCommand(cmd) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func (c *Compiler) CompileElf(binFile string, options map[string]bool, |
| objFiles string) error { |
| binFile += ".elf" |
| return c.CompileBinary(binFile, options, objFiles) |
| } |
| |
| func (c *Compiler) CompileArchive(archiveFile string, objFiles string) error { |
| objList := c.getObjFiles(objFiles) |
| |
| log.Printf("[INFO] Compiling archive %s with object files %s", |
| archiveFile, objList) |
| |
| cmd := c.arPath + " rcs " + archiveFile + " " + objList |
| |
| _, err := ShellCommand(cmd) |
| return err |
| } |