blob: edc109672a390677804e28b1613f4d037b46c7aa [file] [log] [blame]
/*
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
}