diff --git a/.gitignore b/.gitignore index 6f84fcd..f48287d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ out .direnv .envrc result -mutants.* -_out_* \ No newline at end of file +mutants.* \ No newline at end of file diff --git a/build.rs b/build.rs index 615d00f..83e9641 100644 --- a/build.rs +++ b/build.rs @@ -4,55 +4,32 @@ //! the out directory. This can be used to use the build script as a command line tool from other //! build tools. -use std::{env, fs}; +use std::{env, fs::copy}; fn main() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - println!("cargo::rerun-if-changed={crate_dir}/src"); - println!("cargo::rerun-if-changed={crate_dir}/build.rs"); - println!("cargo::rerun-if-changed={crate_dir}/Cargo.toml"); - println!("cargo::rerun-if-changed={crate_dir}/Cargo.lock"); - println!("cargo::rerun-if-changed={crate_dir}/cbindgen.toml"); - println!("cargo::rerun-if-env-changed=SERVICEPOINT_HEADER_OUT"); + println!("cargo::rerun-if-changed={crate_dir}"); let config = cbindgen::Config::from_file(crate_dir.clone() + "/cbindgen.toml") .unwrap(); + let output_dir = env::var("OUT_DIR").unwrap(); let header_file = output_dir.clone() + "/servicepoint.h"; - let bindings = match cbindgen::generate_with_config(crate_dir, config) { - Ok(bindings) => bindings, - Err(e) => { - eprintln!("cargo:warning=Servicepoint header could not be generated: {e:?}"); - return; + if let Ok(bindings) = cbindgen::generate_with_config(crate_dir, config) { + bindings.write_to_file(&header_file); + + println!("cargo:include={output_dir}"); + + println!("cargo::rerun-if-env-changed=SERVICEPOINT_HEADER_OUT"); + if let Ok(header_out) = env::var("SERVICEPOINT_HEADER_OUT") { + let header_copy = header_out + "/servicepoint.h"; + println!("cargo:warning=Copying header to {header_copy}"); + copy(header_file, &header_copy).unwrap(); + println!("cargo::rerun-if-changed={header_copy}"); } - }; - - bindings.write_to_file(&header_file); - println!("cargo:include={output_dir}"); - - if let Ok(header_out) = env::var("SERVICEPOINT_HEADER_OUT") { - let header_copy = header_out + "/servicepoint.h"; - - if fs::exists(&header_copy).ok().unwrap_or(false) { - // check if content changed to prevent rebuild of dependents if not - - let mut bindings_text = Vec::new(); - bindings.write(&mut bindings_text); - - match fs::read(&header_copy) { - Ok(old_content) if old_content == bindings_text => { - println!("cargo:warning=Header did not change, not updating timestamp"); - return; - } - _ => {} - }; - } - - // file does not exist or is different - println!("cargo:warning=Copying header to {header_copy}"); - fs::copy(header_file, &header_copy).unwrap(); - println!("cargo::rerun-if-changed={header_copy}"); + } else { + eprintln!("cargo:warning=Servicepoint header could not be generated"); } } diff --git a/example/Makefile b/example/Makefile index db73873..0a865ac 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,136 +1,137 @@ -# based on https://make.mad-scientist.net/papers/multi-architecture-builds/ - -_libc := $(if $(LIBC),$(LIBC),gnu) -ifeq (,$(filter gnu musl,$(_libc))) - _link_type := $(error "LIBC has to be set to one of: gnu, musl") -endif - -_link_type := $(if $(LINK),$(LINK),dynamic) -ifeq (,$(filter dynamic static,$(_link_type))) - _link_type := $(error "LINK has to be set to one of: dynamic, static") -endif - -_profile := $(if $(PROFILE),$(PROFILE),release) -ifeq (,$(filter release debug size-optimized,$(_profile))) - _profile := $(error "PROFILE has to be set to one of: debug, release, size-optimized") -endif - -ARCH ?= $(shell uname -m) -RUST_TARGET := $(ARCH)-unknown-linux-$(_libc) - -#----- Begin Boilerplate -ifeq (,$(filter _out_%,$(notdir $(CURDIR)))) - include target.mk -else -#----- End Boilerplate - -VPATH = $(SRCDIR) - CARGO ?= cargo STRIP ?= strip -CC ?= gcc -FEATURES := ""#"env_logger" +FEATURES := "env_logger" THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) REPO_ROOT := $(realpath $(THIS_DIR)/..) +export SERVICEPOINT_HEADER_OUT := $(REPO_ROOT)/include -# TODO: check if override is needed -_rust_cli_profile := $(if $(filter $(_profile),debug),dev,$(_profile)) +override CFG_MUSL := $(if $(CFG_MUSL),$(CFG_MUSL),$(if $(MUSL),$(MUSL),0)) +override CFG_PROFILE := $(if $(CFG_PROFILE),$(CFG_PROFILE),$(if $(PROFILE),$(PROFILE),release)) -# TODO: `make LINK=static` fails with linker error "undefined reference to `_dl_find_object'" in libgcc_eh.a +CCFLAGS += -Wall -Wextra -pedantic -fwhole-program -fPIE -pie + +STRIPFLAGS := -s --strip-unneeded -R .comment -R .gnu.version -R .note -R .note.gnu.build-id -R .note.ABI-tag + +#ifeq ($(CFG_PROFILE), size-optimized) +# CCFLAGS += -nodefaultlibs -lc +#endif + +STATIC_LINK_LIBS := -lservicepoint_binding_c + +ifeq ($(CFG_PROFILE), size-optimized) + CARGO_PROFILE := size-optimized + CCFLAGS += -Oz \ + -fwrapv -fomit-frame-pointer -fno-stack-protector\ + -fno-unroll-loops \ + -fno-unwind-tables -fno-asynchronous-unwind-tables \ + -fmerge-all-constants \ + -Wl,-z,norelro \ + -Wl,--hash-style=gnu \ + -fvisibility=hidden \ + -Bsymbolic \ + -Wl,--exclude-libs,ALL \ + -fno-ident \ + -fno-exceptions + CARGOFLAGS += -Zbuild-std="core,std,alloc,proc_macro,panic_abort" \ + -Zbuild-std-features="panic_immediate_abort" + RUSTFLAGS += -Zlocation-detail=none \ + -Zfmt-debug=none \ + -C link-arg=-z,norelro \ + -C panic=abort + #-C link-arg=--hash-style=gnu +else + FEATURES := $(FEATURES),all_compressions + STATIC_LINK_LIBS += -llzma + ifeq ($(CFG_PROFILE), release) + CARGO_PROFILE := release + CCFLAGS += -O2 + else ifeq ($(CFG_PROFILE), debug) + CCFLAGS += -Og + CARGO_PROFILE := dev + else + CFG_PROFILE := $(error "PROFILE has to be set to one of: debug, release, size-optimized") + endif +endif + + +ifeq ($(CFG_MUSL), 1) + TARGET ?= x86_64-unknown-linux-musl + CC ?= musl-gcc + CCFLAGS += -static $(STATIC_LINK_LIBS) + RUSTFLAGS += --crate-type=staticlib -Ctarget-feature=-crt-static +else + TARGET ?= x86_64-unknown-linux-gnu + CC ?= gcc + #CCFLAGS += -shared + CCFLAGS += -Wl,-Bstatic $(STATIC_LINK_LIBS) -Wl,-Bdynamic +endif -STRIPFLAGS += -s --strip-unneeded -R .comment -R .gnu.version -R .note -R .note.gnu.build-id -R .note.ABI-tag -STATIC_LINK_LIBS += -lservicepoint_binding_c -size-optimized_CARGOFLAGS += -Zbuild-std="core,std,alloc,proc_macro,panic_abort" \ - -Zbuild-std-features="panic_immediate_abort" CARGOFLAGS += --manifest-path=$(REPO_ROOT)/Cargo.toml \ - --profile=$(_rust_cli_profile) \ + --profile=$(CARGO_PROFILE) \ --no-default-features \ --features=$(FEATURES) \ - --target=$(RUST_TARGET) \ - $($(_profile)_CARGOFLAGS) \ - --target-dir=cargo + --target=$(TARGET) -CFLAGS += -Wall -Wextra -pedantic -fwhole-program -fPIE -pie -_no_debug_cflags := -ffunction-sections -fdata-sections -Wl,--gc-sections -size-optimized_CFLAGS += -Oz \ - -fwrapv -fomit-frame-pointer -fno-stack-protector\ - -fno-unroll-loops \ - -fno-unwind-tables -fno-asynchronous-unwind-tables \ - -fmerge-all-constants \ - -Wl,-z,norelro \ - -Wl,--hash-style=gnu \ - -fvisibility=hidden \ - -Bsymbolic \ - -Wl,--exclude-libs,ALL \ - -fno-ident \ - -fno-exceptions \ - $(_no_debug_cflags) -release_CFLAGS += -O2 \ - $(_no_debug_cflags) -debug_CFLAGS += -Og -static_CFLAGS += -static $(STATIC_LINK_LIBS) -dynamic_CFLAGS += -Wl,-Bstatic $(STATIC_LINK_LIBS) -Wl,-Bdynamic - -size-optimized_RUSTFLAGS += -Zlocation-detail=none \ - -Zfmt-debug=none \ - -C link-arg=-z,norelro \ - -C panic=abort - #-C link-arg=--hash-style=gnu -musl_RUSTFLAGS += --crate-type=staticlib -Ctarget-feature=-crt-static - -RUSTFLAGS += $($(_libc)_RUSTFLAGS) $($(_profile)_RUSTFLAGS) $($(_link_type)_RUSTFLAGS) -ifneq ($(_profile), debug) +ifneq ($(CFG_PROFILE), debug) + CCFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections RUSTFLAGS += -C link-arg=-s -C link-arg=-Wl,--gc-sections endif -CARGO_OBJDIR := cargo/$(RUST_TARGET)/$(_profile) -_servicepoint_cflags := -I $(REPO_ROOT)/include -L $(CARGO_OBJDIR) - -CFLAGS += $($(_libc)_CFLAGS) $($(_profile)_CFLAGS) $($(_link_type)_CFLAGS) $(_servicepoint_cflags) ifeq ($(LTO), 1) - CFLAGS += -flto + CCFLAGS += -flto endif +RUST_TARGET_DIR := $(REPO_ROOT)/target/$(TARGET)/$(CFG_PROFILE) -# TODO: wildcard does not work with VPATH -_c_src := src/announce.c src/brightness_tester.c src/header_logger.c \ - src/moving_line.c src/random_stuff.c src/wiping_clear.c +_c_src := $(wildcard ./src/*.c) _programs := $(basename $(notdir $(_c_src))) -_bins := $(_programs) +_bins := $(addprefix out/, $(_programs)) _unstripped_bins := $(addsuffix _unstripped, $(_bins)) _run_programs := $(addprefix run_, $(_programs)) - -# TODO: use cargo --target-dir to put rust binaries into same dir +_rs_src := $(wildcard ../src/**.rs) ../Cargo.lock ../Cargo.toml ../cbindgen.toml +_sp_artifacts := $(SERVICEPOINT_HEADER_OUT)/servicepoint.h $(RUST_TARGET_DIR)/libservicepoint_binding_c.a $(RUST_TARGET_DIR)/libservicepoint_binding_c.so all: $(_bins) -.PHONY: all clean sizes $(_run_programs) clean-c clean-rust build-rust help FORCE +clean: clean-c clean-rust -$(_unstripped_bins): %_unstripped: src/%.c src/helpers.h build-rust - $(CC) $< $(CFLAGS) -o $@ +clean-c: + rm -r out || true -$(_bins): %: %_unstripped +clean-rust: + rm $(SERVICEPOINT_HEADER_OUT)/servicepoint.h || true + cargo clean + +.PHONY: all clean sizes $(_run_programs) clean-c clean-rust + +$(_unstripped_bins): out/%_unstripped: src/%.c $(SERVICEPOINT_HEADER_OUT)/servicepoint.h src/helpers.h $(_sp_artifacts) + mkdir -p out || true + $(CC) $< \ + -I $(SERVICEPOINT_HEADER_OUT) \ + -L $(RUST_TARGET_DIR) \ + $(CCFLAGS) \ + -o $@ + +$(_bins): out/%: out/%_unstripped $(STRIP) $(STRIPFLAGS) $^ -o $@ -build-rust: FORCE +$(_sp_artifacts): $(_rs_src) + mkdir -p $(SERVICEPOINT_HEADER_OUT) || true # generate servicepoint header and library to link against $(CARGO) rustc $(CARGOFLAGS) -- $(RUSTFLAGS) +$(_run_programs): run_%: out/% FORCE + ./$< + +sizes: $(_bins) + ls -lB out + +analyze-size: out/$(BIN)_unstripped + nm --print-size --size-sort --reverse-sort --radix=d --demangle out/$(BIN)_unstripped \ + | awk '{size=$$2+0; print size "\t" $$4}' \ + | less + FORCE: ; - -help: - @echo "You have the choice of the following parameters:" - @echo "" - @echo "Variable | Description | Default | Values" - @echo "---------+-----------------------------------+-----------+---------------------------" - @echo "CARGO | which cargo binary to use | 'cargo' | 'rustup run nightly cargo'" - @echo "CC | which C compiler to use | 'gcc' | 'musl-gcc'" - @echo "STRIP | which strip command to use | 'strip' | -" - @echo "MUSL | if set, use musl instead of glibc | unset | '1'" - @echo "PROFILE | Compilation profile | 'release' | 'debug' or 'size-optimized'" - -#----- Begin Boilerplate -endif \ No newline at end of file diff --git a/example/src/header_logger.c b/example/src/header_logger.c index 93c17dd..a4ea051 100644 --- a/example/src/header_logger.c +++ b/example/src/header_logger.c @@ -16,7 +16,7 @@ void handle_error(const char *msg) { } int main(int argc, char **argv) { - //init_env_logger(); + init_env_logger(); int udp_socket = socket(AF_INET, SOCK_DGRAM, 0); if (udp_socket == -1) diff --git a/example/target.mk b/example/target.mk deleted file mode 100644 index bdf84e0..0000000 --- a/example/target.mk +++ /dev/null @@ -1,38 +0,0 @@ -# Based on https://make.mad-scientist.net/papers/multi-architecture-builds/ - -.SUFFIXES: - -OBJDIR := _out_$(RUST_TARGET)-$(_link_type)-$(_profile) - -# Define the rules to build in the target subdirectories. -# -MAKETARGET = $(MAKE) --no-print-directory -C $@ -f $(CURDIR)/Makefile \ - SRCDIR=$(CURDIR) $(MAKECMDGOALS) - -.PHONY: $(OBJDIR) -$(OBJDIR): - +@[ -d "$@" ] || mkdir -p "$@" - +@$(MAKETARGET) - -# These rules keep make from trying to use the match-anything rule below to -# rebuild the makefiles--ouch! Obviously, if you don't follow my convention -# of using a `.mk' suffix on all non-standard makefiles you'll need to change -# the pattern rule. -# -Makefile : ; -%.mk :: ; - -# Anything we don't know how to build will use this rule. The command is a -# do-nothing command, but the prerequisites ensure that the appropriate -# recursive invocations of make will occur. -# -% :: $(OBJDIR) ; - -# The clean rule is best handled from the source directory: since we're -# rigorous about keeping the target directories containing only target files -# and the source directory containing only source files, `clean' is as trivial -# as removing the target directories! -# -.PHONY: clean -clean: - rm -rf $(OBJDIR) diff --git a/flake.lock b/flake.lock index c5f76d2..ad77a01 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1748037224, - "narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=", + "lastModified": 1747862697, + "narHash": "sha256-U4HaNZ1W26cbOVm0Eb5OdGSnfQVWQKbLSPrSSa78KC0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f09dede81861f3a83f7f06641ead34f02f37597f", + "rev": "2baa12ff69913392faf0ace833bc54bba297ea95", "type": "github" }, "original": {