From 2eb3a5adf2fd55f21dd9dfa20b423883a6afce12 Mon Sep 17 00:00:00 2001 From: Andreas Fankhauser (@tux-six) Date: Tue, 5 Jul 2022 03:20:13 +0200 Subject: Enhance configure script and other cleanup - Get rid of Makefile by using configure script to create it fully - Enhance automation of PROJECT_VERSION - Fold README and doc/INSTALL into one file - Try to be more POSIX-like in scripts - Rename 'package' target to 'dist' - Drop usage of 'bool' - Count dirs also while dry-run - Use dbl quotes in some log msgs --- .gitignore | 1 + Makefile | 91 ---------------------------------- README.txt | 16 +++++- configure | 133 ++++++++++++++++++++++++++++++++++++++++++++------ doc/INSTALL.txt | 14 ------ src/bulk_ln/bulk_ln.c | 57 +++++++++++----------- 6 files changed, 164 insertions(+), 148 deletions(-) delete mode 100644 Makefile delete mode 100644 doc/INSTALL.txt diff --git a/.gitignore b/.gitignore index 2247d5f..a3cd4a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/Makefile /build /dist diff --git a/Makefile b/Makefile deleted file mode 100644 index 86b113f..0000000 --- a/Makefile +++ /dev/null @@ -1,91 +0,0 @@ - -CC=gcc -TAR=tar -MKDIRS=mkdir -p -TOOLS=lxGcc64 - -# See "./configure" -PREFIX=/usr/local -EXEC_PREFIX=$(PREFIX) -EXEC_SUFFIX= -BINDIR=$(EXEC_PREFIX)/bin - -ifndef PROJECT_VERSION - # We just provide a primitive version string so we can see which build - # we're using while debugging. For a release we will override the version - # by providing it from cli args to 'make' as in: - # make clean package PROJECT_VERSION=1.2.3 - PROJECT_VERSION=$(shell date -u +0.0.0-%Y%m%d.%H%M%S) -endif - -CFLAGS= --std=c99 \ - -Wall -Wextra -Werror -fmax-errors=3 \ - -Wno-error=unused-function -Wno-error=unused-label \ - -Wno-error=unused-variable -Wno-error=unused-parameter \ - -Wno-error=unused-const-variable \ - -Werror=implicit-fallthrough=1 \ - -Wno-error=unused-but-set-variable \ - -Wno-unused-function -Wno-unused-parameter \ - -DPROJECT_VERSION=$(PROJECT_VERSION) - -LDFLAGS= -Wl,--no-demangle,--fatal-warnings - -INCDIRS= -Isrc/bulk_ln -Isrc/common - -ifndef NDEBUG - CFLAGS := $(CFLAGS) -ggdb -O0 -g3 -else - CFLAGS := $(CFLAGS) -ffunction-sections -fdata-sections -Os "-DNDEBUG=1" - LDFLAGS := $(LDFLAGS) -Wl,--gc-sections,--as-needed -endif - - -default: link package - -.PHONY: clean -clean: - @echo "\n[INFO ] Clean" - rm -rf build dist - -.PHONY: link -link: build/bin/bulk-ln$(EXEC_SUFFIX) - -build/obj/%.o: src/%.c - @echo "\n[INFO ] Compile '$@'" - @mkdir -p $(shell dirname build/obj/$*) - $(CC) -c -o $@ $< $(CFLAGS) $(INCDIRS) - -build/bin/bulk-ln$(EXEC_SUFFIX): \ - build/obj/bulk_ln/bulk_ln.o \ - build/obj/bulk_ln/bulk_ln_main.o - @echo "\n[INFO ] Link '$@'" - @mkdir -p $(shell dirname $@) - $(CC) -o $@ $(LDFLAGS) $^ $(LIBSDIR) - -.PHONY: package -package: link - @echo "\n[INFO ] Package" - @rm -rf build/dist-* dist - @mkdir dist - @echo - @bash -c 'if [[ -n `git status --porcelain` ]]; then echo "[WARN ] Worktree not clean!"; sleep 3; fi' - @# Create Executable bundle. - @rm -rf build/dist-bin && mkdir -p build/dist-bin - @cp -t build/dist-bin \ - README* - @mkdir build/dist-bin/bin - @cp -t build/dist-bin/bin \ - build/bin/*$(EXEC_SUFFIX) - @(cd build/dist-bin && find . -type f -not -name MD5SUM -exec md5sum -b {} \;) > build/MD5SUM - @mv build/MD5SUM build/dist-bin/. - @(cd build/dist-bin && $(TAR) --owner=0 --group=0 -czf ../../dist/BulkLn-$(PROJECT_VERSION)-$(TOOLS).tgz *) - @echo "\n[INFO ] DONE: Artifacts created and placed in 'dist'." - @echo - @echo See './dist/' for result. - -.PHONY: install -install: - @$(MKDIRS) "$(BINDIR)" - $(TAR) -f "$(shell ls dist/BulkLn-*-$(TOOLS).tgz)" \ - -C "$(BINDIR)" --strip-components=1 -x bin - diff --git a/README.txt b/README.txt index 91f2c2f..1239604 100644 --- a/README.txt +++ b/README.txt @@ -6,5 +6,19 @@ BulkLn Usage: bulk-ln --help -Build: "doc/INSTALL.txt" + +How to build / install +********************** + +Use the well-known procedure: + + curl -sSL http://git.hiddenalpha.ch/bulk-ln.git/snapshot/bulk-ln-master.tar.gz | tar xz + cd bulk-ln-master + ./configure + make + make install + +You can get some help with: + + ./configure --help diff --git a/configure b/configure index fea5aeb..419e9a8 100755 --- a/configure +++ b/configure @@ -1,16 +1,18 @@ -#!/usr/bin/env sh + set -e +set -o posix # See https://www.gnu.org/prep/standards/html_node/Directory-Variables.html prefix=/usr/local exec_prefix='$(PREFIX)' exec_suffix= bindir='$(EXEC_PREFIX)/bin' +verbose=0 main() { parseArgs "$@" - mangleMakefile + printMakefile > Makefile } @@ -19,10 +21,11 @@ printHelp() { echo "Example usage: $0 --prefix /usr/local" echo '' echo 'Options:' - echo ' --prefix Default "/usr/local"' - echo ' --exec-prefix Default "$(PREFIX)"' - echo ' --exec-suffix Default ' - echo ' --bindir Default "$(EXEC_PREFIX)/bin"' + echo ' --prefix "/usr/local"' + echo ' --exec-prefix "$(PREFIX)"' + echo ' --exec-suffix ""' + echo ' --bindir "$(EXEC_PREFIX)/bin"' + echo ' --verbose 0' echo '' } @@ -31,25 +34,127 @@ parseArgs() { # See: https://stackoverflow.com/a/14203146/4415884 while test $# -gt 0 ; do case $1 in - --help) printHelp; exit 1; ;; + --help) printHelp; exit 1; ;; --prefix) prefix="$2" shift; shift; ;; --exec-prefix) exec_prefix="$2" shift; shift; ;; --exec-suffix) exec_suffix="$2" shift; shift; ;; --bindir) bindir="$2" shift; shift; ;; + --verbose) verbose="$2" shift; shift; ;; *) echo "[ERROR] Unexpected argument: $1"; exit 1 ;; esac done + if test "$verbose" != "0" -a "$verbose" != "1"; then + echo "[ERROR] verbose expected to be 0 or 1. But is: $verbose"; + false; + fi } -mangleMakefile() { - sed -i 's;^PREFIX=.*$;PREFIX='"$prefix"';' Makefile - sed -i 's;^EXEC_PREFIX=.*$;EXEC_PREFIX='"$exec_prefix"';' Makefile - sed -i 's;^EXEC_SUFFIX=.*$;EXEC_SUFFIX='"$exec_suffix"';' Makefile - sed -i 's;^BINDIR=.*$;BINDIR='"$bindir"';' Makefile -} +printMakefile () { + printf '\n' + printf '%s\n' '# This file is generated by configure script. Manual changes may be lost' + printf '%s\n' '# when calling configure again.' + printf '\n' + printf '%s\n' 'CC=gcc' + printf '%s\n' 'TAR=tar' + printf '%s\n' 'MKDIRS=mkdir -p' + printf '%s\n' 'RIMRAF=rm -rf' + printf '%s\n' 'COPYTO=cp -t' + printf '%s\n' "PREFIX=$prefix" + printf '%s\n' "EXEC_PREFIX=$exec_prefix" + printf '%s\n' "EXEC_SUFFIX=$exec_suffix" + printf '%s\n' "BINDIR=$bindir" + # If PROJECT_VERSION set, use it. + # If not, try generate via git describe. + # If fails (eg git not avail), create pseudo version using date. + printf '%s' 'PROJECT_VERSION=$(shell' + printf '%s' ' ret=$$(git describe --tags 2>/dev/null |sed -E "s;^v;;");' + printf '%s' ' if test -z "$$ret"; then ret=$$(date -u +0.0.0-%Y%m%d.%H%M%S); fi;' + printf '%s\n' ' echo "$$ret")' -main "$@" + printf '\n' + printf '%s' 'CFLAGS= --std=c99' + printf '%s' ' -Wall -Wextra -Werror -fmax-errors=3' + printf '%s' ' -Wno-error=unused-function -Wno-error=unused-label' + printf '%s' ' -Wno-error=unused-variable -Wno-error=unused-parameter' + printf '%s' ' -Wno-error=unused-const-variable' + printf '%s' ' -Werror=implicit-fallthrough=1' + printf '%s' ' -Wno-error=unused-but-set-variable' + printf '%s' ' -Wno-unused-function -Wno-unused-parameter' + printf '%s\n' ' -DPROJECT_VERSION=$(PROJECT_VERSION)' + printf '%s\n' '' + printf '%s\n' 'LDFLAGS= -Wl,--no-demangle,--fatal-warnings' + printf '%s\n' '' + printf '%s\n' 'INCDIRS= -Isrc/bulk_ln -Isrc/common' + printf '%s\n' '' + printf '%s\n' 'ifndef NDEBUG' + printf '%s\n' ' CFLAGS := $(CFLAGS) -ggdb -O0 -g3' + printf '%s\n' 'else' + printf '%s\n' ' CFLAGS := $(CFLAGS) -ffunction-sections -fdata-sections -Os "-DNDEBUG=1"' + printf '%s\n' ' LDFLAGS := $(LDFLAGS) -Wl,--gc-sections,--as-needed' + printf '%s\n' 'endif' + + if test "$verbose" = "0"; then + printf '\n' + printf '%s\n' '.SILENT:'; + fi + + printf '\n' + printf '%s\n' 'default: link dist' + + printf '\n' + printf '%s\n' '.PHONY: clean' + printf '%s\n' 'clean:' + printf '%s\n' ' echo $(RIMRAF) build dist' + printf '%s\n' ' $(RIMRAF) build dist' + + printf '\n' + printf '%s\n' '.PHONY: link' + printf '%s\n' 'link: build/bin/bulk-ln$(EXEC_SUFFIX)' + + printf '\n' + printf '%s\n' 'build/obj/%.o: src/%.c' + printf '%s\n' ' echo " CC $@"' + printf '%s\n' ' $(MKDIRS) "$(shell T=$@; echo $${T%/*})"' + printf '%s\n' ' $(CC) -c -o $@ $^ $(CFLAGS) $(INCDIRS)' + printf '\n' + printf '%s' 'build/bin/bulk-ln$(EXEC_SUFFIX):' + # dependencies + printf '%s' ' build/obj/bulk_ln/bulk_ln.o' + printf '%s' ' build/obj/bulk_ln/bulk_ln_main.o' + printf '\n' # commands + printf '%s\n' ' echo " LN $@"' + printf '%s\n' ' $(MKDIRS) "$(shell T=$@; echo $${T%/*})"' + printf '%s\n' ' $(CC) -o $@ $(LDFLAGS) $^ $(LIBSDIR)' + printf '\n' + printf '%s\n' '.PHONY: dist' + printf '%s\n' 'dist: link' + printf '%s\n' ' echo " Package"' + printf '%s\n' ' $(RIMRAF) build/dist-* dist' + printf '%s\n' ' $(MKDIRS) dist' + # Create Executable bundle. + printf '%s\n' ' $(MKDIRS) build/dist-bin && $(COPYTO) build/dist-bin \' + printf '%s\n' ' README.txt' + printf '%s\n' ' $(MKDIRS) build/dist-bin/bin && $(COPYTO) build/dist-bin/bin \' + printf '%s\n' ' build/bin/*' + + printf '%s\n' ' (cd build/dist-bin && find . -type f -not -name MD5SUM -exec md5sum -b {} +) > build/dist-bin/MD5SUM' + + printf '%s\n' ' (cd build/dist-bin && $(TAR) --owner=0 --group=0 -czf "../../dist/BulkLn-$(PROJECT_VERSION).tgz" *)' + printf '%s\n' ' echo "" && echo "See '\''./dist/'\'' for result." && echo ""' + printf '%s' ' if test -n "$$(git status --porcelain 2>/dev/null)"; then' + printf '%s' ' echo "[WARN ] Worktree dirty!"; sleep 2;' + printf '%s\n' ' fi' + + printf '\n' + printf '%s\n' '.PHONY: install' + printf '%s\n' 'install:' + printf '%s\n' ' $(MKDIRS) "$(BINDIR)"' + printf '%s\n' ' $(TAR) --strip-components=1 -C "$(BINDIR)" -xzf "$(shell ls dist/BulkLn-*.tgz)" -- bin' +} + + +main "$@" diff --git a/doc/INSTALL.txt b/doc/INSTALL.txt deleted file mode 100644 index 68ebc59..0000000 --- a/doc/INSTALL.txt +++ /dev/null @@ -1,14 +0,0 @@ - -How to build/install -==================== - -Use the well-known procedure: - - ./configure - make - make install - -You can get some help with: - - ./configure --help - diff --git a/src/bulk_ln/bulk_ln.c b/src/bulk_ln/bulk_ln.c index 6d6b6a3..682c1aa 100644 --- a/src/bulk_ln/bulk_ln.c +++ b/src/bulk_ln/bulk_ln.c @@ -1,24 +1,21 @@ -/* Header for this file */ +/* This */ #include "bulk_ln.h" /* System */ #include #include -#include // TODO remove #include #include #include #include #include -/* Other packages from this project */ -/*#include ""*/ - #define DATA_FILE_FIELD_SEP_CHR '\t' +typedef char bool; typedef struct BulkLn BulkLn; @@ -31,6 +28,12 @@ struct BulkLn { FILE *dataFd; + /** Count of links we created */ + int createdLinksCount; + + /** Count of direcrories we created */ + int createdDirsCount; + /** If a dry-run got requested */ bool dryRun; @@ -48,25 +51,20 @@ struct BulkLn { /** if true, we print a summary what we did at the end of the run to stderr */ bool isPrintSummary; - /** if true, we will override existing files. This will be on for example - * if --force got specified. */ + /** if true, we will override existing files. This will be enabled for + * example if --force got specified. */ bool isRelinkExistingFiles; - - /** Count of links we created */ - int createdLinksCount; - - /** Count of direcrories we created */ - int createdDirsCount; }; -void printHelp(){ +static void printHelp(){ printf("\n %s%s\n", strrchr(__FILE__, '/') + 1, " @ " STR_QUOT(PROJECT_VERSION) "\n" "\n" "Utility to create links. Writing a custom implementation of 'ln' got necessary\n" "as we found no way to instruct 'ln' to create a few thousand links in an\n" - "acceptable amount of time. So we just wrote our own ;)\n" + "acceptable amount of time. So we just re-invented that wheel so it better fits\n" + "our use-case ;)\n" "\n" "Takes paths (pairwise) from stdin (see --stdin for details) and creates a\n" "hardlink for each pair from the 1st path to the 2nd.\n" @@ -95,9 +93,7 @@ void printHelp(){ "\n" " --dry-run\n" " Will print the actions to stdout instead executing them.\n" - " HINT: The directory count in the summary will be implicitly set to\n" - " zero, as our used counting strategy would deliver wrong results when we\n" - " not actually creating the dirs.\n" + " HINT: Directory count in summary will be inaccurate in this mode.\n" "\n" " --force\n" " Same meaning as in original 'ln' command.\n" @@ -107,7 +103,7 @@ void printHelp(){ /** returns non-zero on errors. Error messages will already be printed * internally. */ -int parseArgs( int argc, char**argv, BulkLn*bulkLn ){ +static int parseArgs( int argc, char**argv, BulkLn*bulkLn ){ /* init (aka set defaults) */ bulkLn->dataFilePath = NULL; bulkLn->dryRun = 0; @@ -118,7 +114,7 @@ int parseArgs( int argc, char**argv, BulkLn*bulkLn ){ bulkLn->isRelinkExistingFiles = 0; // Parse args - for( int i=1 ; iisRelinkExistingFiles = !0; } else if( !strcmp(arg, "--quiet") ){ - bulkLn->isPrintStatus = false; - bulkLn->isPrintSummary = false; + bulkLn->isPrintStatus = 0; + bulkLn->isPrintSummary = 0; } else if( !strcmp(arg, "--stdin") ){ bulkLn->dataFilePath = "-"; @@ -216,6 +212,12 @@ static int mkdirs( char*path, BulkLn*bulkLn ){ * the dir. Eg if it did NOT already exist */ bulkLn->createdDirsCount += 1; } + }else{ + /* We cannot really count how many dirs we created. Because maybe + * we are (hypothetically) creating this directory the 100th time + * now. But we have no way to tell as we do not really create em. + * So just increment to have some value at all */ + bulkLn->createdDirsCount += 1; } if( tmpEnd == pathEnd ){ @@ -240,7 +242,7 @@ static int createHardlink( char*srcPath, char*dstPath, BulkLn*bulkLn ){ int err; if( bulkLn->dryRun || bulkLn->isPrintEachCreateLink ){ - printf("link('%s', '%s')\n", srcPath, dstPath); + printf("link(\"%s\", \"%s\")\n", srcPath, dstPath); } if( ! bulkLn->dryRun ){ @@ -262,7 +264,7 @@ static int createHardlink( char*srcPath, char*dstPath, BulkLn*bulkLn ){ } err = link(srcPath, dstPath); if( err ){ - fprintf(stderr, "link('%s', '%s'): %s\n", srcPath, dstPath, strerror(errno)); + fprintf(stderr, "link(\"%s\", \"%s\"): %s\n", srcPath, dstPath, strerror(errno)); err = -1; goto finally; } } @@ -293,7 +295,7 @@ static int onPathPair( char*srcPath, char*dstPath, BulkLn*bulkLn ){ tmpEnd[0] = '\0'; /* Create missing parent dirs */ err = mkdirs(dstPath, bulkLn); - if (err) { err = -1; goto finally; } + if( err ){ err = -1; goto finally; } /* Restore path */ tmpEnd[0] = '/'; } @@ -350,7 +352,7 @@ static int parseDataFileAsPairPerLine( BulkLn*bulkLn ){ char *dstPath = tab + 1; /* <- path starts one char after the separator */ char *dstPath_end = buf + buf_len; for(;; --dstPath_end ){ - if( dstPath_end < buf ){ + if(unlikely( dstPath_end < buf )){ fprintf(stderr, "IMHO cannot happen (@%s:%d)\n", __FILE__, __LINE__); err = -1; goto finally; } @@ -388,7 +390,6 @@ int bulk_ln_main( int argc, char**argv ){ BulkLn bulkLn = {0}; #define bulkLn (&bulkLn) - /* parse args */ err = parseArgs(argc, argv, bulkLn); if( err ){ err = -1; goto finally; } @@ -414,7 +415,7 @@ int bulk_ln_main( int argc, char**argv ){ err = 0; finally: if( bulkLn->dataFd != NULL && bulkLn->dataFd != stdin ){ - fclose(bulkLn->dataFd); bulkLn->dataFd = NULL; + fclose(bulkLn->dataFd); } return err; #undef bulkLn -- cgit v1.1