mirror of
https://github.com/danog/gllvm.git
synced 2024-11-30 07:28:58 +01:00
197 lines
6.1 KiB
Go
197 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"io/ioutil"
|
|
"log"
|
|
"strings"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"io"
|
|
)
|
|
|
|
func compile(args []string) {
|
|
if len(args) < 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)
|
|
|
|
// 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 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) {
|
|
// We can only attach a bitcode path to certain file types
|
|
switch filepath.Ext(objFile) {
|
|
case
|
|
".o",
|
|
".lo",
|
|
".os",
|
|
".So",
|
|
".po":
|
|
// Store bitcode path to temp file
|
|
var absBcPath, _= filepath.Abs(bcFile)
|
|
tmpContent := []byte(absBcPath+"\n")
|
|
tmpFile, err := ioutil.TempFile("", "gowllvm")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer os.Remove(tmpFile.Name())
|
|
if _, err := tmpFile.Write(tmpContent); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := tmpFile.Close(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Let's write the bitcode section
|
|
var attachCmd string
|
|
var attachCmdArgs []string
|
|
if runtime.GOOS == "darwin" {
|
|
attachCmd = "ld"
|
|
attachCmdArgs = []string{"-r", "-keep_private_externs", objFile, "-sectcreate", DARWIN_SEGMENT_NAME, DARWIN_SECTION_NAME, tmpFile.Name(), "-o", objFile}
|
|
} else {
|
|
attachCmd = "objcopy"
|
|
attachCmdArgs = []string{"--add-section", ELF_SECTION_NAME+"="+tmpFile.Name(), objFile}
|
|
}
|
|
|
|
// Run the attach command and ignore errors
|
|
execCmd(attachCmd, attachCmdArgs)
|
|
|
|
// Copy bitcode file to store, if necessary
|
|
if bcStorePath := os.Getenv(BC_STORE_PATH); bcStorePath != "" {
|
|
destFilePath := path.Join(bcStorePath, getHashedPath(absBcPath))
|
|
in, _ := os.Open(absBcPath)
|
|
defer in.Close()
|
|
out, _ := os.Create(destFilePath)
|
|
defer out.Close()
|
|
io.Copy(out, in)
|
|
out.Sync()
|
|
}
|
|
}
|
|
}
|
|
|
|
func linkFiles(compilerExecName string, pr ParserResult, objFiles []string) {
|
|
var outputFile = pr.OutputFilename
|
|
if outputFile == "" {
|
|
outputFile = "a.out"
|
|
}
|
|
args := append(pr.ObjectFiles, pr.LinkArgs...)
|
|
args = append(args, objFiles...)
|
|
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, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if cmd.Run() == nil {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
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 ""
|
|
}
|
|
}
|