Skip to content

Commit

Permalink
Cleaned up some of the code
Browse files Browse the repository at this point in the history
  • Loading branch information
codecat committed Jul 12, 2020
1 parent 05ed15f commit 8cda32d
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 113 deletions.
35 changes: 35 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

// Context contains all the build system states that have to be remembered.
type Context struct {
// Name is the name of the project.
Name string

// SourceFiles contains paths to all the .c and .cpp files that have to be compiled.
SourceFiles []string

// ObjectPath is the intermediate folder where object files should be stored.
ObjectPath string

// Compiler is an abstract interface used for compiling and linking on multiple platforms.
Compiler Compiler
CompilerErrors int
CompilerOptions *CompilerOptions
CompilerWorkerChannel chan CompilerWorkerTask
CompilerWorkerFinished chan int
}

// NewContext creates a new context with initial values.
func NewContext() (*Context, error) {
compiler, err := getCompiler()
if err != nil {
return nil, err
}

return &Context{
Compiler: compiler,
CompilerOptions: &CompilerOptions{},

SourceFiles: make([]string, 0),
}, nil
}
138 changes: 25 additions & 113 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"

Expand All @@ -14,38 +13,6 @@ import (
"github.com/spf13/viper"
)

type workerTask struct {
path string
outputDir string
options *CompilerOptions
}

var compiler Compiler
var compilerErrors int

var workerChan chan workerTask
var workerFinished chan int

func compileWorker(num int) {
for {
task, ok := <-workerChan
if !ok {
break
}

fileForward := strings.Replace(task.path, "\\", "/", -1)
log.Info("%s", fileForward)

err := compiler.Compile(task.path, task.outputDir, task.options)
if err != nil {
log.Error("Failed to compile %s!\n%s", fileForward, err.Error())
compilerErrors++
}
}

workerFinished <- num
}

func hasCommand(cmd string) bool {
for _, arg := range os.Args {
if strings.HasPrefix(arg, "--") {
Expand Down Expand Up @@ -81,126 +48,71 @@ func main() {
log.Info("Using build configuration file %s", filepath.Base(viper.ConfigFileUsed()))
}

// Load any compiler options
options := CompilerOptions{
Static: viper.GetBool("static"),
Debug: viper.GetBool("debug"),
Verbose: viper.GetBool("verbose"),
}

// Find the compiler
compiler, err = getCompiler()
// Prepare qb's internal context
ctx, err := NewContext()
if err != nil {
log.Fatal("Unable to get compiler: %s", err.Error())
log.Fatal("Unable to initialize context: %s", err.Error())
return
}

// Find the name of the project
name := viper.GetString("name")
if name == "" {
ctx.Name = viper.GetString("name")
if ctx.Name == "" {
// If there's no name set, use the name of the current directory
currentDir, _ := filepath.Abs(".")
name = filepath.Base(currentDir)
ctx.Name = filepath.Base(currentDir)
}

// If we have to clean, do that and exit
// If we only have to clean, do that and exit
if hasCommand("clean") {
compiler.Clean(name)
ctx.Compiler.Clean(ctx.Name)
return
}

// Load any compiler options
ctx.CompilerOptions.Static = viper.GetBool("static")
ctx.CompilerOptions.Debug = viper.GetBool("debug")
ctx.CompilerOptions.Verbose = viper.GetBool("verbose")

// Find all the source files to compile
sourceFiles, err := getSourceFiles()
ctx.SourceFiles, err = getSourceFiles()
if err != nil {
log.Fatal("Unable to read directory: %s", err.Error())
return
}

if len(sourceFiles) == 0 {
if len(ctx.SourceFiles) == 0 {
log.Warn("No source files found!")
return
}

// Make a temporary folder for .obj files
pathTmp := filepath.Join(os.TempDir(), fmt.Sprintf("qb_%d", time.Now().Unix()))
os.Mkdir(pathTmp, 0777)
defer os.RemoveAll(pathTmp)

// Prepare worker channel
workerChan = make(chan workerTask)
workerFinished = make(chan int)

// Start compiler worker routines
numWorkers := runtime.NumCPU()
if len(sourceFiles) < numWorkers {
numWorkers = len(sourceFiles)
}
for i := 0; i < numWorkers; i++ {
go compileWorker(i)
}
ctx.ObjectPath = filepath.Join(os.TempDir(), fmt.Sprintf("qb_%d", time.Now().Unix()))
os.Mkdir(ctx.ObjectPath, 0777)
defer os.RemoveAll(ctx.ObjectPath)

// Begin compilation timer
// Perform the compilation
timeStart := time.Now()

// Compile all the source files
for _, file := range sourceFiles {
dir := filepath.Dir(file)
outputDir := filepath.Join(pathTmp, dir)

err := os.MkdirAll(outputDir, 0777)
if err != nil {
log.Error("Unable to create output directory %s: %s", outputDir, err.Error())
compilerErrors++
continue
}

workerChan <- workerTask{
path: file,
outputDir: outputDir,
options: &options,
}
}

// Close the worker channel
close(workerChan)

// Wait for all workers to finish compiling
for i := 0; i < numWorkers; i++ {
<-workerFinished
}

// Measure the time that compilation took
performCompilation(ctx)
timeCompilation := time.Since(timeStart)

// Stop if there were any compiler errors
if compilerErrors > 0 {
if ctx.CompilerErrors > 0 {
log.Fatal("😢 Compilation failed!")
return
}

// Link
linkType := LinkExe
switch viper.GetString("type") {
case "exe":
linkType = LinkExe
case "dll":
linkType = LinkDll
case "lib":
linkType = LinkLib
}

// Begin link timer
// Perform the linking
timeStart = time.Now()
outPath, err := performLinking(ctx)
timeLinking := time.Since(timeStart)

outPath, err := compiler.Link(pathTmp, name, linkType, &options)
// Stop if linking failed
if err != nil {
log.Fatal("👎 Link failed: %s", err.Error())
return
}

// Measure the time that linking took
timeLinking := time.Since(timeStart)

// Report succcess
log.Info("👏 %s", outPath)
log.Info("⏳ compile %v, link %v", timeCompilation, timeLinking)
Expand Down
82 changes: 82 additions & 0 deletions main_compile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"os"
"path/filepath"
"runtime"
"strings"

"github.com/codecat/go-libs/log"
)

// CompilerWorkerTask describes a task for the compiler worker
type CompilerWorkerTask struct {
path string
outputDir string
}

func compileWorker(ctx *Context, num int) {
for {
// Get a task
task, ok := <-ctx.CompilerWorkerChannel
if !ok {
break
}

// Log the file we're currently compiling
fileForward := strings.Replace(task.path, "\\", "/", -1)
log.Info("%s", fileForward)

// Invoke the compiler
err := ctx.Compiler.Compile(task.path, task.outputDir, ctx.CompilerOptions)
if err != nil {
log.Error("Failed to compile %s!\n%s", fileForward, err.Error())
ctx.CompilerErrors++
}
}

ctx.CompilerWorkerFinished <- num
}

func performCompilation(ctx *Context) {
// Prepare worker channels
ctx.CompilerWorkerChannel = make(chan CompilerWorkerTask)
ctx.CompilerWorkerFinished = make(chan int)

// Start compiler worker routines
numWorkers := runtime.NumCPU()
if len(ctx.SourceFiles) < numWorkers {
numWorkers = len(ctx.SourceFiles)
}
for i := 0; i < numWorkers; i++ {
go compileWorker(ctx, i)
}

// Compile all the source files
for _, file := range ctx.SourceFiles {
// The output dir will be a sub-folder in the object directory
dir := filepath.Dir(file)
outputDir := filepath.Join(ctx.ObjectPath, dir)

err := os.MkdirAll(outputDir, 0777)
if err != nil {
log.Error("Unable to create output directory %s: %s", outputDir, err.Error())
ctx.CompilerErrors++
continue
}

// Send the task to an available worker
ctx.CompilerWorkerChannel <- CompilerWorkerTask{
path: file,
outputDir: outputDir,
}
}

// Close the worker channel
close(ctx.CompilerWorkerChannel)

// Wait for all workers to finish compiling
for i := 0; i < numWorkers; i++ {
<-ctx.CompilerWorkerFinished
}
}
21 changes: 21 additions & 0 deletions main_link.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"github.com/spf13/viper"
)

func performLinking(ctx *Context) (string, error) {
// Get the link type
linkType := LinkExe
switch viper.GetString("type") {
case "exe":
linkType = LinkExe
case "dll":
linkType = LinkDll
case "lib":
linkType = LinkLib
}

// Invoke the linker
return ctx.Compiler.Link(ctx.ObjectPath, ctx.Name, linkType, ctx.CompilerOptions)
}

0 comments on commit 8cda32d

Please sign in to comment.