Skip to content

ameershira/craftc

Repository files navigation

craftc — Build when it matters

craftc

craftc is a small command-line build tool for C projects. It compiles object files, creates static libraries, and links executables. It is designed to be called directly or from tools such as Make and Task.

It tracks source files, headers, and command-line changes so unchanged targets do not need to be rebuilt.

Go Reference GitHub go.mod Go version GitHub Workflow Status GitHub

What it does

  • Rebuilds objects when a source file, header, or compile command changes
  • Compiles multiple source files concurrently
  • Creates static libraries with ar
  • Links executables and relinks when an input library changes
  • Accepts custom compiler and linker flags
  • Provides optional verbose output

Linux is supported. macOS and Windows have not been tested.

How craftc decides to rebuild

For each object file, craftc checks whether:

  • The object file exists
  • Its dependency file exists
  • Its recorded compile command exists
  • The source file is newer than the object
  • Any recorded project dependency is newer than the object
  • The compile command, compiler, or compiler flags have changed

When linking an executable, craftc checks whether:

  • The executable exists
  • Any application object was rebuilt during the current run
  • Its recorded link command exists
  • Any linked library is newer than the executable
  • The link command, compiler, library list, or linker flags have changed

A static library is recreated when:

  • The archive does not exist
  • Any of its object files was rebuilt during the current run
  • A forced build was requested

Passing --force bypasses the up-to-date checks and rebuilds the requested target completely.

Module path change

This module was previously published as:

github.com/ameergituser/craftc

New versions are published under:

github.com/ameershira/craftc

Installation

Install with Go:

go install github.com/ameershira/craftc@latest

Or clone and build it locally:

git clone https://github.com/ameershira/craftc
cd craftc
go build .

Usage examples

These examples use -v to print the commands being run.

1. Build an object file

Use the obj command:

craftc obj -cc cc -cfile ./libsrc1.c -cflags "-Wall -O2" -objdir ./build/obj -v

Output:

[build] 🧠 build/obj/libsrc1.c.o: object file does not exist.
[compile] 🔨 /usr/bin/cc -Wall -O2 -MMD -MF build/obj/libsrc.libsrc1.c.d -c ./libsrc1.c -o build/obj/libsrc1.c.o

Re-run:

✅ build/obj/libsrc1.c.o is up to date.

2. Build multiple object files concurrently

Use the objs command:

craftc objs -cc cc -cfiles "./libsrc1.c ./libsrc2.c" -cflags "-Wall" -objdir ./build/obj -v

Output:

[build] 🧠 build/obj/libsrc2.c.o: object file does not exist.
[build] 🧠 build/obj/libsrc1.c.o: object file does not exist.
[compile] 🔨 /usr/bin/cc -Wall -MMD -MF build/obj/libsrc2.c.d -c ./libsrc2.c -o build/obj/libsrc2.c.o
[compile] 🔨 /usr/bin/cc -Wall -MMD -MF build/obj/libsrc1.c.d -c ./libsrc1.c -o build/obj/libsrc1.c.o

3. Build a static library

Use the static-lib command:

craftc static-lib -cc cc -cfiles "./libsrc1.c ./libsrc2.c" -cflags "-Wall -O2" -lib-path "./build/lib.a" -objdir ./build/obj -v

Output:

[build] 🧠 build/obj/libsrc2.c.o: object file does not exist.
[build] 🧠 build/obj/libsrc1.c.o: object file does not exist.
[compile] 🔨 /usr/bin/cc -Wall -O2 -MMD -MF build/obj/libsrc2.c.d -c ./libsrc2.c -o build/obj/libsrc2.c.o
[compile] 🔨 /usr/bin/cc -Wall -O2 -MMD -MF build/obj/libsrc1.c.d -c ./libsrc1.c -o build/obj/libsrc1.c.o
[archive] 📦 /usr/bin/ar rcs ./build/lib.a build/obj/libsrc1.c.o build/obj/libsrc2.c.o

Re-run:

✅ build/obj/libsrc2.c.o is up to date.
✅ build/obj/libsrc1.c.o is up to date.
✅ 📦 ./build/lib.a is up to date.

4. Build an executable

This example also links a static library:

craftc exe -cc cc -cfiles "main.c" -objdir ./build/obj -cflags "-Wall -O2 -I ./libsrc" -lib-paths "./build/lib.a" -exe-path ./app

Output:

[build] 🧠 build/obj/main.c.o: object file does not exist.
[compile] 🔨 /usr/bin/cc -Wall -O2 -I ./libsrc -MMD -MF build/obj/main.c.d -c ./main.c -o build/obj/main.c.o
[linking] 🔗 /usr/bin/cc build/obj/main.c.o ./build/lib.a -o ./build/app

Re-run:

✅ build/obj/main.c.o is up to date.
✅ 🚀 ./build/app is up to date.

5. Task integration

Each command can be used on its own, which makes craftc straightforward to call from Task or another task runner. This Taskfile builds a static library before linking the executable:

version: '3'

tasks:

  lib:
    desc: Build a static library with a few source files
    vars:
      BUILD_DIR: ./build/{{.TASK}}
      OBJ_DIR: '{{.BUILD_DIR}}/obj'
      SRC: ./libsrc/libsrc1.c ./libsrc/libsrc2.c
      STATIC_LIB: '{{.BUILD_DIR}}/{{.TASK}}.a'
      CFLAGS: -Wall -O2
    cmds:
      - ./craftc static-lib -cc cc -cfiles "{{.SRC}}" -objdir {{.OBJ_DIR}} -cflags "{{.CFLAGS}}" -lib-path "{{.STATIC_LIB}}" -i {{.CLI_ARGS}}


  exe:
    desc: Build an exe cmd with a few source files
    vars:
      BUILD_DIR: ./build/{{.TASK}}
      OBJ_DIR: '{{.BUILD_DIR}}/obj'
      SRC: ./appsrc/main.c
      STATIC_LIB: ./build/lib/lib.a
      CFLAGS: -Wall -O2 -I ./libsrc
      # LDFLAGS: -Wl,--trace
      APP_PATH: '{{.BUILD_DIR}}/{{.TASK}}-app'
    deps:
      - task: lib
    cmds:
      - >
        ./craftc exe
        -cc cc
        -cfiles "{{.SRC}}"
        -objdir {{.OBJ_DIR}}
        -cflags "{{.CFLAGS}}"
        -ldflags "{{.LDFLAGS}}"
        -exe-path {{.APP_PATH}}
        -lib-paths "{{.STATIC_LIB}}"
        -i {{.CLI_ARGS}}

Output:

[build] 🧠 build/lib/obj/libsrc.libsrc2.c.o: object file does not exist.
[build] 🧠 build/lib/obj/libsrc.libsrc1.c.o: object file does not exist.
[compile] 🔨 /usr/bin/cc -Wall -O2 -MMD -MF build/lib/obj/libsrc.libsrc1.c.d -c ./libsrc/libsrc1.c -o build/lib/obj/libsrc.libsrc1.c.o
[compile] 🔨 /usr/bin/cc -Wall -O2 -MMD -MF build/test4/obj/libsrc.libsrc2.c.d -c ./libsrc/libsrc2.c -o build/lib/obj/libsrc.libsrc2.c.o
[archive] 📦 /usr/bin/ar rcs ./build/lib/lib.a build/lib/obj/libsrc.libsrc1.c.o build/lib/obj/libsrc.libsrc2.c.o
[build] 🧠 build/exe/obj/appsrc.main.c.o: object file does not exist.
[compile] 🔨 /usr/bin/cc -Wall -O2 -I ./libsrc -MMD -MF build/exe/obj/appsrc.main.c.d -c ./appsrc/main.c -o build/exe/obj/appsrc.main.c.o
[linking] 🔗 /usr/bin/cc build/exe/obj/appsrc.main.c.o ./build/lib/lib.a -o ./build/exe/app

Re-run:

✅ build/lib/obj/libsrc.libsrc2.c.o is up to date.
✅ build/lib/obj/libsrc.libsrc1.c.o is up to date.
✅ 📦 ./build/lib/lib.a is up to date.
✅ build/exe/obj/appsrc.main.c.o is up to date.
✅ 🚀 ./build/exe/app is up to date.

About

Fast, Simple, Minimal C Build Tool

Resources

License

Stars

1 star

Watchers

1 watching

Forks

Packages

 
 
 

Contributors

Languages