I am currently trying to write a build script for a c++ project in Nushell.
It's a fairly simple setup, with a library that is built first and then and exe that is build in an other script and linked agaisnt the library. My problem is more obvious when building the library so I'm only going to show this.
The build script for the library is mostly about defining the flags and calling a build command defined in an other file :
# Build script for stowy physics engine
source ../common.nu
def main [buildType: string = 'debug'] {
let isRelease = $buildType == 'release'
# Setup flags
let compilerFlags = [ '-std=c++20' (if $isRelease { '-O3' } else { '-g3' })]
const includeFlags = ['-Isrc']
const linkerFlags = ['-shared']
let defines = ['-DSTWEXPORT' (if $isRelease { '-DNDEBUG'} else { '-DDEBUG' })]
build stowy_physics_engine 'src/' '../bin/' $compilerFlags $includeFlags $linkerFlags $defines $buildType library
}
And this is what common.nu looks like :
# Builds the assembly according to the parameters
def build [
assemblyName: string,
sourceDirectory: path,
binaryDirectory: path,
compilerFlags: list<string>,
includeFlags: list<string>,
linkerFlags: list<string>,
defines: list<string>,
buildType: string = 'debug', # The optimisation level of the build. Can be 'release' or 'debug'.
targetType: string = 'executable', # The type of target. Can be 'executable' or 'library'.
] {
let isRelease = $buildType == 'release'
let assemblyExtension = if $targetType == 'executable' {
if ($env.OS | str contains 'Windows') { '.exe' } else { '' }
} else if $targetType == 'library' {
if ($env.OS | str contains 'Windows') { '.dll' } else { '.so' }
} else {
return 'Invalid targetType'
}
let assemblyOutputFile = $'($binaryDirectory)/($assemblyName)($assemblyExtension)'
let cppFiles = glob **/*.cpp
let relativeCppFiles = ($cppFiles | path relative-to $sourceDirectory | path parse)
# Build every .cpp to .o and get a list of every .o
let objectFiles = $relativeCppFiles | each {|relativeCppFile| (
# Convert from path object to string
let inputFile = ($relativeCppFile | path join);
let outputFile = ($relativeCppFile | upsert extension 'o' | path join);
# Recreate the folder structure of .cpp files for the .o in the binary directory
let inputDirectory = ($relativeCppFile | get parent);
mkdir ($'($binaryDirectory)/($inputDirectory)');
# concatenate the .o file with the bin directory
let outputFileWithDir = $'($binaryDirectory)/($outputFile)'; # ex: ../bin/math/Vector2.o
let sourceFileWithDir = $'($sourceDirectory)/($inputFile)'; # ex: src/math/Vector2.cpp
clang++ -c -o $outputFileWithDir $sourceFileWithDir $defines $includeFlags $compilerFlags;
$outputFileWithDir
)}
let startTime = date now
clang++ -o $assemblyOutputFile $objectFiles $linkerFlags $includeFlags $defines $compilerFlags
print $'Built and linked ($assemblyName) successfully.'
}
As you can see, it finds every .cpp
file with a recursive search and build each of them with a call to clang (my goal is to then cache this build step with ccache).
Then, after the loop, I give each .o
file to clang to link the .dll
(or .so
on linux, but I haven't tested there yet).
My problem is that when I empty the bin directory before start this command, it seems like the link command is called before the compilation is really finished and it can't find any of the .o
.
Building stowy_physics_engine in debug...
Building C:\dev\cpp\build_system_tests\bin/collision\BroadPhaseGrid.o...
...
Building C:\dev\cpp\build_system_tests\bin/math\Vector2.o...
Built objects in 266ms 309µs 700ns
Linking...
clang++: error: no such file or directory: 'C:\dev\cpp\build_system_tests\bin/collision\BroadPhaseGrid.o'
...
clang++: error: no such file or directory: 'C:\dev\cpp\build_system_tests\bin/math\Vector2.o'
clang++: error: no input files
Any idea on what could be causing this ?
I was able to solve this problem by calling clang
with run-external
and printing the output :
print --no-newline (run-external --redirect-combine clang++ '-c' '-o' $outputFileWithDir $sourceFileWithDir $defines $includeFlags $compilerFlags);