From e14e7e328c499e29f1ab63a91f8f58d61220f645 Mon Sep 17 00:00:00 2001 From: Loic Gelle Date: Thu, 22 Jun 2017 12:14:00 -0700 Subject: [PATCH] [WIP] implement compiling part --- compiler.go | 142 ++++++++++++++++++++++++++++++++++++++++++++++++--- constants.go | 12 +++++ gowllvm.go | 11 ++-- parser.go | 21 ++++++++ 4 files changed, 173 insertions(+), 13 deletions(-) diff --git a/compiler.go b/compiler.go index 0c56731..3565dc5 100644 --- a/compiler.go +++ b/compiler.go @@ -1,19 +1,149 @@ package main import ( - "fmt" "os" + "os/exec" + "log" + "strings" ) func compile(args []string) { if len(args) < 1 { - fmt.Println("You must precise which compiler to use.") - os.Exit(1) + log.Fatal("You must precise which compiler to use.") } var compilerName = args[0] + var compilerExecName = getCompilerExecName(compilerName) + var configureOnly bool + if os.Getenv(CONFIGURE_ONLY) != "" { + configureOnly = true + } args = args[1:] - var pr = parse(args) - var _ = pr - var _ = compilerName + + // If configure only is set, try to execute normal compiling command then exit silently + if configureOnly { + execCompile(compilerExecName, pr) + os.Exit(0) + } + // Else try to build objects and bitcode + buildAndAttachBitcode(compilerExecName, pr) +} + +// Compiles bitcode files and attach path to the object files +func buildAndAttachBitcode(compilerExecName string, pr ParserResult) { + // If nothing to do, exit silently + if len(pr.InputFiles) == 0 || pr.IsEmitLLVM || pr.IsAssembly || pr.IsAssembleOnly || + (pr.IsDependencyOnly && !pr.IsCompileOnly) || pr.IsPreprocessOnly { + os.Exit(0) + } + + var newObjectFiles []string + var hidden = !pr.IsCompileOnly + + if len(pr.InputFiles) == 1 && pr.IsCompileOnly { + var srcFile = pr.InputFiles[0] + objFile, bcFile := getArtifactNames(pr, 0, hidden) + buildObjectFile(compilerExecName, pr, srcFile, objFile) + buildBitcodeFile(compilerExecName, pr, srcFile, bcFile) + attachBitcodePathToObject(bcFile, objFile) + } else { + for i, srcFile := range pr.InputFiles { + objFile, bcFile := getArtifactNames(pr, i, hidden) + buildObjectFile(compilerExecName, pr, srcFile, objFile) + if hidden { + newObjectFiles = append(newObjectFiles, objFile) + } else if strings.HasSuffix(srcFile, ".bc") { + attachBitcodePathToObject(srcFile, objFile) + } else { + buildBitcodeFile(compilerExecName, pr, srcFile, bcFile) + attachBitcodePathToObject(bcFile, objFile) + } + } + } + + if !pr.IsCompileOnly { + linkFiles(compilerExecName, pr, newObjectFiles) + } +} + +func attachBitcodePathToObject(bcFile, objFile string) { + // TODO +} + +func linkFiles(compilerExecName string, pr ParserResult, objFiles []string) { + var outputFile = pr.OutputFilename + if outputFile == "" { + outputFile = "a.out" + } + args := append(pr.ObjectFiles, objFiles...) + args = append(args, pr.LinkArgs...) + args = append(args, "-o", outputFile) + if execCmd(compilerExecName, args) { + log.Fatal("Failed to link.") + } +} + +// Tries to build the specified source file to object +func buildObjectFile(compilerExecName string, pr ParserResult, srcFile string, objFile string) { + args := pr.CompileArgs[:] + args = append(args, srcFile, "-c", "-o", objFile) + if execCmd(compilerExecName, args) { + log.Fatal("Failed to build object file for", srcFile) + } +} + +// Tries to build the specified source file to bitcode +func buildBitcodeFile(compilerExecName string, pr ParserResult, srcFile string, bcFile string) { + args := pr.CompileArgs[:] + args = append(args, "-emit-llvm", "-c", srcFile, "-o", bcFile) + if execCmd(compilerExecName, args) { + log.Fatal("Failed to build bitcode file for", srcFile) + } +} + +// Tries to build object file +func execCompile(compilerExecName string, pr ParserResult) { + if execCmd(compilerExecName, pr.InputList) { + log.Fatal("Failed to execute compile command.") + } +} + +// Executes a command then returns true if there was an error +func execCmd(cmdExecName string, args []string) bool { + cmd := exec.Command(cmdExecName, listToArgString(args)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if cmd.Run() == nil { + return false + } else { + return true + } +} + +// Joins a list of arguments to create a string +func listToArgString(argList []string) string { + return strings.Join(argList, " ") +} + +func getCompilerExecName(compilerName string) string { + var compilerPath = os.Getenv(COMPILER_PATH) + switch compilerName { + case "clang": + var clangName = os.Getenv(C_COMPILER_NAME) + if clangName != "" { + return compilerPath + clangName + } else { + return compilerPath + compilerName + } + case "clang++": + var clangppName = os.Getenv(C_COMPILER_NAME) + if clangppName != "" { + return compilerPath + clangppName + } else { + return compilerPath + compilerName + } + default: + log.Fatal("The compiler", compilerName, "is not supported by this tool.") + return "" + } } diff --git a/constants.go b/constants.go index 06ab7d0..0602b18 100644 --- a/constants.go +++ b/constants.go @@ -1 +1,13 @@ package main + +// Environment variables +var CONFIGURE_ONLY = "GOWLLVM_CONFIGURE_ONLY" +var COMPILER_PATH = "GOWLLVM_COMPILER_PATH" +var C_COMPILER_NAME = "GOWLLVM_CC_NAME" +var CXX_COMPILER_NAME = "GOWLLVM_CXX_NAME" +var BC_STORE_PATH = "GOWLLVM_BC_STORE" + +// Gowllvm functioning +var ELF_SECTION_NAME = ".llvm_bc" +var DARWIN_SEGMENT_NAME = "__WLLVM" +var DARWIN_SECTION_NAME = "__llvm_bc" diff --git a/gowllvm.go b/gowllvm.go index d59481b..6fe568d 100644 --- a/gowllvm.go +++ b/gowllvm.go @@ -1,16 +1,15 @@ package main import( - "fmt" "os" + "log" ) func main() { // Parse command line var args = os.Args if len(args) < 2 { - fmt.Println("Not enough arguments.") - os.Exit(1) + log.Fatal("Not enough arguments.") } var modeFlag = args[1] args = args[2:] @@ -20,10 +19,8 @@ func main() { // Call main compile function with args compile(args) case "extract": - fmt.Println("The extract feature is not implemented yet.") - os.Exit(1) + log.Fatal("The extract feature is not implemented yet.") default: - fmt.Println("You should call gowllvm with a valid mode.") - os.Exit(1) + log.Fatal("You should call gowllvm with a valid mode.") } } diff --git a/parser.go b/parser.go index 3965d01..6ec4335 100644 --- a/parser.go +++ b/parser.go @@ -4,6 +4,8 @@ import( "fmt" "regexp" "runtime" + "path/filepath" + "strings" ) type ParserResult struct { @@ -215,6 +217,25 @@ func parse(argList []string) ParserResult { return pr } +// Return the object and bc filenames that correspond to the i-th source file +func getArtifactNames(pr ParserResult, srcFileIndex int, hidden bool) (objBase string, bcBase string) { + if len(pr.InputFiles) == 1 && pr.IsCompileOnly && len(pr.OutputFilename) > 0 { + objBase = pr.OutputFilename + bcBase = fmt.Sprintf(".%s.bc", objBase) + } else { + srcFile := pr.InputFiles[srcFileIndex] + var baseNameWithExt = filepath.Base(srcFile) + var baseName = strings.TrimSuffix(baseNameWithExt, filepath.Ext(baseNameWithExt)) + bcBase = fmt.Sprintf(".%s.o.bc", baseName) + if hidden { + objBase = fmt.Sprintf(".%s.o", baseName) + } else { + objBase = fmt.Sprintf("%s.o", baseName) + } + } + return +} + func inputFileCallback(pr ParserResult, flag string, _ []string) { var regExp = regexp.MustCompile(`\\.(s|S)$`) pr.InputFiles = append(pr.InputFiles, flag)