Compiling Cpp and C files into shared objects using Zig cc/c++

I’m trying to create a cross-platform way to build tree-sitter grammars for Emacs using Zig. I have a PowerShell Script written that downloads the right Zig version, then downloads a list of tree-sitter libraries. For each library it searches for all the .c files and dynamically links them. Unfortunately, I’ve discovered some repos use .cc files as well. So, I’ve added logic to compile the .cc files into .o files, but I’m running into this error when I try to compile everything.

Cloning into 'C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml'...
remote: Enumerating objects: 324, done.
remote: Counting objects: 100% (83/83), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 324 (delta 69), reused 62 (delta 62), pack-reused 241
Receiving objects: 100% (324/324), 1.22 MiB | 6.62 MiB/s, done.
Resolving deltas: 100% (157/157), done.
Compiling C++ files with the command: zig c++ -c -o libcpp.o C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\scanner.cc C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\schema.generated.cc -Isrc -Isrc/tree-sitter
error: coff does not support linking multiple objects into one
Compiling C files with the command: zig cc -c -o libc.o C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\parser.c -Isrc -Isrc/tree-sitter
Compiling final shared library with the command: zig cc -shared -o libtree-sitter-yaml.dll libcpp.o libc.o
LLD Link... lld-link: error: could not open 'libcpp.o': No such file or directory
Move-Item: C:\home\emacs.d\tree-sitter-install.ps1:172

For those who can’t read PowerShell, these are the commands that are being run

zig c++ -c -o libcpp.o C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\scanner.cc C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\schema.generated.cc -Isrc -Isrc/tree-sitter

 zig cc -c -o libc.o C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\parser.c -Isrc -Isrc/tree-sitter

 zig cc -shared -o libtree-sitter-yaml.dll libcpp.o libc.o

Here is the relevant part of the script

foreach ($url in $grammarUrls) {
    $repoName = $url -split '/' | Select-Object -Last 1
    # I.E tree-sitter-markdown to markdown
    $languageName = $repoName -replace 'tree-sitter-', ''
    $cloneDir = Join-Path $tempDir $repoName

    # Check if grammar directory already exists and -ReinstallGrammar is not used
    if ((Test-Path -Path $cloneDir) -and (-not $ReinstallGrammar)) {
        Write-Host "$repoName already cloned. Pass -ReinstallGrammar to reclone."
        continue
    }

    # If -ReinstallGrammar is used, clean up the current grammar directory
    if ($ReinstallGrammar -And (Test-Path -Path $cloneDir)) {
        Write-Host "Removing old version of $repoName"
        Remove-Item -Path $cloneDir -Recurse -Force
    }

    Write-Host "Downloaded $url to $cloneDir"
    # Clone the grammar repository
    git clone $url $cloneDir

    # Navigate to the cloned directory
    Push-Location -Path $cloneDir

    # Compile the grammar using zig cc
    $libName = "libtree-sitter-$languageName$fileExt"
    $cSrcFiles = (Get-ChildItem src/*.c -Recurse).FullName -join ' '
    $cppSrcFiles = (Get-ChildItem src/*.cc -Recurse).FullName -join ' '
    $includeDirs = @('-Isrc -Isrc/tree-sitter')

    # Check if there are C++ source files
if ($cppSrcFiles -ne $null) {
    # Compile C++ files using zig c++
    $cppObj = "libcpp.o"
    $cppCmd = "zig c++ -c -o $cppObj $cppSrcFiles $($includeDirs -join ' ')"
    Write-Host "Compiling C++ files with the command: $cppCmd"
    Invoke-Expression $cppCmd

    $cObj = "libc.o"
    $cCmd= "zig cc -c -o $cObj $cSrcFiles $($includeDirs -join ' ')"
    Write-Host "Compiling C files with the command: $cCmd"
    Invoke-Expression $cCmd

    # Link the files together into a shared object file
    $zigCmd = "zig dlltool -shared -o $libName $cppObj $cObj"
    Write-Host "Compiling final shared library with the command: $zigCmd"
    Invoke-Expression $zigCmd
}
    else {
    # Construct the zig cc command for compiling C files
    $zigCmd = "zig cc -shared -o $libName $cSrcFiles $($includeDirs -join ' ')"
    Write-Host "Compiling C files with the command: $zigCmd"
    Invoke-Expression $zigCmd
    }

I’m waay out of my element right now as a Zig/C/C++ noob. Can anyone point me in the right direction?

EDIT
I think I got it

if ($cppSrcFiles -ne $null) {
    # Compile C++ files using zig c++
    [System.Collections.ArrayList]$cppObjectFiles = @()
    foreach ($fileName in $cppSrcFiles) {
        $cppObject = $fileName + ".o"
        $cppObjectFiles.Add($cppObject) | Out-Null
        $cppCmd = "zig c++ -c -fPIC $fileName -o $cppObject $($includeDirs -join ' ' ) -lc"
        Write-Host "Compiling C++ files with the command: $cppCmd`n"
	Invoke-Expression $cppCmd
    }

    [System.Collections.ArrayList]$cObjectFiles = @()
    foreach ($fileName in $cSrcFiles) {
        $cObject = $fileName + ".o"
        $cObjectFiles.Add($cObject) | Out-Null
        $cCmd = "zig cc -c -fPIC $fileName -o $cObject $($includeDirs -join ' ')"
        Write-Host "Compiling C files with the command: $cCmd`n"
	Invoke-Expression $cCmd
    }

    $zigCmd = "zig c++ -shared -o $libName $($cppObjectFiles -join ' ') $($cOjbectFiles -join ' ')"
    Write-Host "Compiling Shared files with the command: $zigCmd`n"
    Invoke-Expression $zigCmd
}

It looks like you’re trying to compile multiple source files into a single “object” .o file: gcc - Compile multiple C source fles into a unique object file - Stack Overflow

I think what you’re inherently trying to do is build a static library (since you’re trying to combine everything into one object file). You need to build those objects separately and then link them into a static library.

It’s this command here: Compiling C++ files with the command: zig c++ -c -o libcpp.o C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\scanner.cc C:\Users\decre\AppData\Local\Temp\tree-sitter-yaml\src\schema.generated.cc -Isrc -Isrc/tree-sitter

If you want to build a dynamic library, that’s a bit different (oddly enough, right?). You probably want to look up the differences between the two and how to build them. I wouldn’t try to avoid the learning curve here, tbh.

For a static library (using a bunch of .o files) you have to do approximately:

  1. build separate object files (you can build multiple at once with -c but you’ll get separate .o’s for each source). Go lookup something like “compile multiple c source files at once” and then you’ll get back a unique .o for every source you provided.
  2. use a command to combine every object file into a static library (on linux, it’s the ar command, not sure about other operating systems).

Again, the issue is you’re directly trying to take multiple sources and combine them all into one libcpp.o file.

Also, welcome to the forums :slight_smile:

1 Like