-1

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 ?

UpAndAdam
  • 4,515
  • 3
  • 28
  • 46
TOOL
  • 70
  • 1
  • 10
  • is there a particular reason you need to invent your own build system instead of using say Make or CMake? oh windows... my point remains.... also you realize you have path slashes going in the wrong direction in your files right? you are mixing '/' and '\' which i imagine the system might not like. – UpAndAdam Aug 30 '23 at 17:37
  • Because I'm doing a project where I implement a compare different build systems in batch, powershell, nushell and cmake (maybe make and bash once I install linux). The goal is to learn more about how people do things because yes people still use batch files in some contexts. As for the slashes, it's true that it's mixed up, but that's not why clang struggles to find the .o files, since when they're already built, there's no problem. I will try to fix it just to see though – TOOL Aug 30 '23 at 18:30
  • powershell/batch/nushell dont really change anything but syntax between each other.. they are all roughly the same : not a build system but a command line build script you put together yourself. Make is a build system, CMake is a build system-build file generator. It could generate for Make, or Ninja or several other engines. There's also things like WAF, meson, bazel, scons; how many of those are on windows i do not know. I was just trying to understand **why** you were inventing one yourself. You gave a good reason: you were investigating for comparison for learning or r&d purposes. – UpAndAdam Aug 30 '23 at 18:52

0 Answers0