diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5a90d46 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,96 @@ +name: build + +on: + release: +# types: [published] + push: + branches: + - master + - next + - 'feat/**' + pull_request: + branches: + - master + - next + - 'feat/**' + +jobs: + build-macos: + runs-on: macos-latest + + steps: + + - name: Checkout source + uses: actions/checkout@v6 + + - name: Build on macOS + run: make all + env: + BUILD_DIR: build/macos + + - name: Archive macOS artifacts + uses: actions/upload-artifact@v6 + with: + name: binaries-macos + path: | + build/macos/**/* + !build/macos/obj/* + + build: + runs-on: ubuntu-latest + + steps: + + - name: Install MinGW + run: | + sudo apt-get install -y --no-install-recommends \ + g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 \ + g++-mingw-w64-i686 binutils-mingw-w64-i686 + + - name: Checkout source + uses: actions/checkout@v6 + + - name: Build on Linux x86_64 (dynamic) + run: make all + env: + BUILD_DIR: build/linux + + - name: Build on Linux x86_64 (static) + run: make all + env: + LDFLAGS: -static + CXXFLAGS: -fPIC + TRY_CXXFLAGS: -ffile-prefix-map=".=./src/bomutils" + BUILD_DIR: build/linux/static + + - name: Build on Windows x86_64 (64-bit) + run: make -f Makefile.x64-win all + + - name: Build on Windows x86 (32-bit) + run: make -f Makefile.x86-win all + + - name: Archive Linux amd64 artifacts + uses: actions/upload-artifact@v6 + with: + name: binaries-linux-x64 + path: | + build/linux/**/* + !build/linux/static/obj/* + !build/linux/obj/* + + - name: Archive Windows 64-bit artifacts + uses: actions/upload-artifact@v6 + with: + name: binaries-win-x64 + path: | + build/win/x64/* + !build/win/x64/obj/* + + - name: Archive Windows 32-bit artifacts + uses: actions/upload-artifact@v6 + with: + name: binaries-win-x86 + path: | + build/win/x86/* + !build/win/x86/obj/* + diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..18c81e2 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,50 @@ +name: release + +on: + workflow_run: + workflows: ["build"] + types: + - completed + +# Make sure the GITHUB_TOKEN has permission to upload to our releases +permissions: + contents: write + +jobs: + + assets: + runs-on: ubuntu-latest + #if: ${{ (github.event.workflow_run.conclusion == 'success') && (github.event.release.tag_name != '') }} + if: ${{ (github.event.workflow_run.conclusion == 'success') }} + + steps: + + - name: Download artifacts + uses: actions/download-artifact@v6 + with: + pattern: binaries-* + path: artifacts/ + # run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: '📦 Publish packages' + # if: ${{ startsWith(github.event.release.tag_name, 'rel-') }} + run: | + cd ${{github.workspace}} + mkdir -p build + find artifacts/ build/ -ls + env -C artifacts/binaries-linux-x64 zip -r ../../build/ubuntu-amd64.zip bin/ man/ + env -C artifacts/binaries-macos zip -r ../../build/macos-arm64.zip bin/ man/ + env -C artifacts/binaries-linux-x64 zip -r ../../build/linux-static-amd64.zip bin/ man/ + env -C artifacts/binaries-win-x64 zip -r ../../build/win32-x64.zip bin/ + env -C artifacts/binaries-win-x86 zip -r ../../build/win32-x86.zip bin/ + [ -n "${{github.event.release.tag_name}}" ] && refname="${{github.event.release.tag_name}}" || refname="${{github.ref_name}}" + gh release upload "$refname" build/ubuntu-amd64.zip + gh release upload "$refname" build/linux-static-amd64.zip + gh release upload "$refname" build/macos-arm64.zip + gh release upload "$refname" build/win32-x64.zip + gh release upload "$refname" build/win32-x86.zip + env: + GITHUB_TOKEN: ${{ github.TOKEN }} + shell: bash + diff --git a/Dockerfile b/Dockerfile index 3198c94..1707274 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM ubuntu:16.04 AS builder RUN apt-get update && apt-get install -y build-essential file COPY . . -RUN LDFLAGS=-static make +RUN LDFLAGS="-static" CXXFLAGS="-fPIC" make BUILD_DIR=build RUN strip build/bin/* RUN ls -l build/bin/* RUN file build/bin/* @@ -16,4 +16,5 @@ RUN build/bin/mkbom dist/ dist/Bom FROM scratch COPY --from=builder build/bin/* /usr/bin/ -COPY --from=builder dist/Bom /Bom \ No newline at end of file +COPY --from=builder build/man/* /usr/man/ +COPY --from=builder dist/Bom /Bom diff --git a/Makefile b/Makefile index d2c2a4f..67dd232 100644 --- a/Makefile +++ b/Makefile @@ -19,15 +19,34 @@ # # Initial work done by Joseph Coffland and Julian Devlin. # Numerous further improvements by Baron Roberts. + CXX=g++ +STRIP=strip PREFIX=/usr - SUFFIX= -CXXFLAGS=-g -O3 -ffile-prefix-map=/home/lbr/code/bomutils=. -fstack-protector-all -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -s -LDFLAGS=-pie -Wl,-z,now,-z,relro + +define check_flag + $(shell echo 'int main() { return 0; }' | $(CXX) $(1) -xc - 2>/dev/null && echo $(1)) +endef + +DESIRABLE_LDFLAGS ?= -Wl,-z,now -Wl,-z,relro $(TRY_LDFLAGS) +DESIRABLE_CXXFLAGS ?= -fstack-protector-all -Wformat -Werror=format-security -s $(TRY_LDFLAGS) + +SUPPORTED_LDFLAGS += $(foreach flag,$(DESIRABLE_LDFLAGS),$(call check_flag,$(flag))) +SUPPORTED_CXXFLAGS += $(foreach flag,$(DESIRABLE_CXXFLAGS),$(call check_flag,$(flag))) + +# These can be overridden with `CXXFLAGS="-something" LDFLAGS="" make target' ... +CXXFLAGS ?= -ffile-prefix-map=".=./src/bomutils" +LDFLAGS ?= -pie +# ... while these will be appended anyway (to also override these use `make target CXXFLAGS=""') +CXXFLAGS := $(CXXFLAGS) -D_FORTIFY_SOURCE=2 $(SUPPORTED_CXXFLAGS) +LDFLAGS := $(LDFLAGS) $(SUPPORTED_LDFLAGS) + LIBS= BIN_DIR=$(PREFIX)/bin MAN_DIR=$(PREFIX)/share/man +BUILD_DIR ?= build/unix +RELEASEZIP_NAME = $(shell uname -sm | tr ' ' _) include build.mk diff --git a/Makefile.x64-win b/Makefile.x64-win new file mode 100644 index 0000000..a0fc7d9 --- /dev/null +++ b/Makefile.x64-win @@ -0,0 +1,35 @@ +# +# Makefile.win - Windows make file +# +# Copyright (C) 2013 Fabian Renn - fabian.renn (at) gmail.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA +# +# Initial work done by Joseph Coffland and Julian Devlin. +# Numerous further improvements by Baron Roberts. + +SUFFIX=.exe +CXX=x86_64-w64-mingw32-g++-win32 +STRIP=x86_64-w64-mingw32-strip +CXXFLAGS=-mwindows -mconsole -DWINDOWS -Wall -fstack-protector-all -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -s +LDFLAGS=-mwindows -mconsole -static-libgcc -static-libstdc++ -pie + +LIBS=-lws2_32 + +BUILD_DIR ?= build/win/x64 +RELEASEZIP_NAME = Windows_64bit +RELEASEZIP_ROOT = bin +RELEASEZIP_CONTENT = . +include build.mk diff --git a/Makefile.win b/Makefile.x86-win similarity index 73% rename from Makefile.win rename to Makefile.x86-win index 7c3f6db..05aa966 100644 --- a/Makefile.win +++ b/Makefile.x86-win @@ -19,10 +19,17 @@ # # Initial work done by Joseph Coffland and Julian Devlin. # Numerous further improvements by Baron Roberts. + SUFFIX=.exe -CXX=i586-mingw32msvc-g++ -CXXFLAGS=-mwindows -mconsole -DWINDOWS -Wall -LDFLAGS=-mwindows -mconsole -static-libgcc -static-libstdc++ +CXX=i686-w64-mingw32-g++-win32 +STRIP=i686-w64-mingw32-strip +CXXFLAGS=-mwindows -mconsole -DWINDOWS -Wall -fstack-protector-all -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -s +LDFLAGS=-mwindows -mconsole -static-libgcc -static-libstdc++ -pie + LIBS=-lws2_32 +BUILD_DIR ?= build/win/x86 +RELEASEZIP_NAME = Windows_32bit +RELEASEZIP_ROOT = bin +RELEASEZIP_CONTENT = . include build.mk diff --git a/README.md b/README.md index deac7e2..1e71909 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,16 @@ _bomutils_ are a set of tools to create Mac OS X installer packages on foreign O Build Instructions ------------------ -1. On UNIX-like OSes, compile the code by executing: `make all`;\ - on Mingw64 for Windows, use `make -f Makefile.win all` instead;\ - there is no support for a native Windows build. -2. Tools become available in the `build/bin` directory after the build. -3. Install the tools by executing the install target on the same Makefile as a privileged user. - + +On UNIX-like OSes, compile the code by executing: `make all`; +tools become available in the `build/unix/bin` directory. Install the tools by +executing `make install` but running as a privileged user. + +For Windows binaries, use Mingw64 for cross-compilation: execute either +`make -f Makefile.x64-win all` or `make -f Makefile.x86-win all` to +build the tools, which will become available in the `build/win/x64` or +`build/win/x86` directory. There is no install target or setup utility for +Windows builds. Usage ----- diff --git a/build.mk b/build.mk index 847fac1..0703c52 100644 --- a/build.mk +++ b/build.mk @@ -19,7 +19,8 @@ # # Initial work done by Joseph Coffland and Julian Devlin. # Numerous further improvements by Baron Roberts. -OPTFLAGS=-O2 -g0 -s + +OPTFLAGS += -O3 -g0 -s APP_SOURCES=\ mkbom.cpp \ @@ -31,11 +32,15 @@ COMMON_SOURCES=\ printnode.cpp \ crc32.cpp -BUILD_DIR=build +BUILD_DIR ?= build BUILD_BIN_DIR=$(BUILD_DIR)/bin BUILD_OBJ_DIR=$(BUILD_DIR)/obj BUILD_MAN_DIR=$(BUILD_DIR)/man +RELEASEZIP_DIR ?= build/release +RELEASEZIP_ROOT ?= . +RELEASEZIP_CONTENT ?= bin man + DOCKER_IMAGE_NAME=bomutils SOURCES=$(APP_SOURCES) $(COMMON_SOURCES) @@ -53,6 +58,14 @@ vpath %.1 man .PHONY: $(APP_NAMES) all install clean dist .PRECIOUS: $(BUILD_OBJ_DIR)/%.o $(BUILD_OBJ_DIR)/%.d +STRIP ?= strip +binutils_strip = $(shell $(STRIP) --help | grep -m1 -- --strip-unneeded | wc -l) +ifeq ($(binutils_strip),1) +STRIP_COMMAND = $(STRIP) --strip-unneeded +else +STRIP_COMMAND = $(STRIP) +endif + all : $(APPS) $(MAN) install : all @@ -73,7 +86,7 @@ $(BUILD_OBJ_DIR)/%.d : %.cpp $(BUILD_BIN_DIR)/%$(SUFFIX) : $(BUILD_OBJ_DIR)/%.o $(COMMON_OBJECTS) @mkdir -p $(BUILD_BIN_DIR) $(CXX) -o $@ $(LDFLAGS) $^ $(LIBS) - @strip --strip-unneeded $@ + $(STRIP_COMMAND) $@ $(BUILD_MAN_DIR)/%.1.gz : %.1 @mkdir -p $(BUILD_MAN_DIR) @@ -91,4 +104,18 @@ clean : rm -rf $(BUILD_DIR) docker-image : - docker build -t bomutils . \ No newline at end of file + docker build -t bomutils . + +docker-extract : + mkdir -p "$(BUILD_DIR)/docker" + CID=$$(docker create --annotation=bomutils=build bomutils cat) ; \ + docker export --output $(BUILD_DIR)/docker/rootfs.tar $$CID ; \ + docker container rm $$CID + tar -C "$(BUILD_DIR)" -xf "$(BUILD_DIR)/docker/rootfs.tar" --strip-components=1 usr/bin usr/man + +release : + mkdir -p "$(RELEASEZIP_DIR)" + rm -f "$(RELEASEZIP_DIR)/$(RELEASEZIP_NAME).zip" + cd "$(BUILD_DIR)/$(RELEASEZIP_ROOT)" ; zip -r "$(abspath $(RELEASEZIP_DIR))/$(RELEASEZIP_NAME).zip" $(RELEASEZIP_CONTENT) +.PHONY: release + diff --git a/src/crc32.cpp b/src/crc32.cpp index e6b9c74..54d97c7 100644 --- a/src/crc32.cpp +++ b/src/crc32.cpp @@ -43,9 +43,8 @@ using namespace std; uint32_t calc_crc32( const char * file_path ) { #if defined(WINDOWS) - OFSTRUCT ignore; - HFILE f = OpenFile( file_path, &ignore, OF_READ ); - if ( f == HFILE_ERROR ) { + HANDLE f = CreateFile( file_path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, NULL); + if ( f == INVALID_HANDLE_VALUE ) { #else int f = open( file_path, O_RDONLY ); if ( f < 0 ) { diff --git a/src/ls4mkbom.cpp b/src/ls4mkbom.cpp index c36cc6a..2617ff9 100644 --- a/src/ls4mkbom.cpp +++ b/src/ls4mkbom.cpp @@ -30,17 +30,19 @@ using namespace std; void usage() { - cout << "Usage: ls4mkbom [-u uid] [-g gid] path" << endl << endl; + cout << "Usage: ls4mkbom [-u uid] [-g gid] [-H] path" << endl << endl; cout << "\t-u\tForce user ID to the specified value" << endl; cout << "\t-g\tForce group ID to the specified value" << endl; + cout << "\t-H\tInclude hidden files" << endl; } int main( int argc, char * argv[] ) { uint32_t uid = UINT_MAX; uint32_t gid = UINT_MAX; + bool includeHidden = false; while (true) { - signed char c = getopt(argc, argv, "hu:g:"); + signed char c = getopt(argc, argv, "hu:g:H"); if (c == -1) { break; } @@ -52,6 +54,9 @@ int main( int argc, char * argv[] ) { case 'g': gid = atol(optarg); break; + case 'H': + includeHidden = true; + break; case 'h': usage(); return 0; @@ -67,6 +72,6 @@ int main( int argc, char * argv[] ) { return 1; } - print_node( cout, argv[optind], uid, gid ); + print_node( cout, argv[optind], uid, gid, includeHidden ); return 0; } diff --git a/src/lsbom.cpp b/src/lsbom.cpp index 22d51e7..9bb530b 100644 --- a/src/lsbom.cpp +++ b/src/lsbom.cpp @@ -264,11 +264,15 @@ int main(int argc, char *argv[]) { uint32_t varDataLength; char *varData = lookup(var->index, &varDataLength); BOMTree *tree = (BOMTree *)varData; - string name = string(var->name, var->length); + // Some BOMs include a trailing zero and account for it in length (e.g. iOS build 15G77) + uint8_t varNameLen = var->length; + while (varNameLen > 0 && var->name[varNameLen-1] == '\0') + --varNameLen; + string name = string(var->name, varNameLen); DEBUG(2, "BOMVar 0x" << hex << ntohl(var->index) << ' ' << name << ':'); - if (strstr(name.c_str(),"Paths") == 0) { + if (name == "Paths") { BOMPaths *paths = (BOMPaths *)lookup(tree->child); typedef map filenames_t; diff --git a/src/mkbom.cpp b/src/mkbom.cpp index 030d4f1..f4ef10b 100644 --- a/src/mkbom.cpp +++ b/src/mkbom.cpp @@ -38,8 +38,8 @@ #include #else #include -#include #endif +#include #include #include "bom.h" @@ -473,19 +473,21 @@ void write_bom( istream & lsbom_file, const string & output_path ) { } void usage() { - cout << "Usage: mkbom [i] [-u uid] [-g gid] source target-bom-file" << endl << endl; + cout << "Usage: mkbom [i] [-u uid] [-g gid] [-H] source target-bom-file" << endl << endl; cout << "\t-i\tTreat source as a file in the format generated by ls4mkbom and lsbom" << endl; cout << "\t-u\tForce user ID to the specified value (incompatible with -i)" << endl; cout << "\t-g\tForce group ID to the specified value (incompatible with -i)" << endl; + cout << "\t-H\tInclude hidden files" << endl; } int main( int argc, char * argv[] ) { uint32_t uid = UINT_MAX; uint32_t gid = UINT_MAX; bool isFileListSource = false; + bool includeHidden = false; while (true) { - signed char c = getopt(argc, argv, "hiu:g:"); + signed char c = getopt(argc, argv, "hiu:g:H"); if (c == -1) { break; } @@ -500,6 +502,9 @@ int main( int argc, char * argv[] ) { case 'g': gid = atol(optarg); break; + case 'H': + includeHidden = true; + break; case 'h': usage(); return 0; @@ -530,7 +535,7 @@ int main( int argc, char * argv[] ) { string buffer; { stringstream ss; - print_node( ss, string( argv[optind] ), uid, gid ); + print_node( ss, string( argv[optind] ), uid, gid, includeHidden ); buffer = ss.str(); } stringstream file_list( buffer ); diff --git a/src/printnode.cpp b/src/printnode.cpp index 978987b..6eb8b58 100644 --- a/src/printnode.cpp +++ b/src/printnode.cpp @@ -39,7 +39,7 @@ using namespace std; /* on unix system_path = path; on windows system_path is the windows native path format of path */ void print_node( ostream & output, const string & base, const string & system_path, const string & path, - uint32_t uid, uint32_t gid) { + uint32_t uid, uint32_t gid, bool includeHidden) { struct stat s; string fullpath( base ); #if defined(WINDOWS) @@ -78,23 +78,28 @@ void print_node( ostream & output, const string & base, const string & system_pa DIR * d = opendir( fullpath.c_str() ); struct dirent * dir; while ( ( dir = readdir( d ) ) != NULL ) { - if ( dir->d_name[0] != '.' ) { + string name = string( dir->d_name ); + if ( !includeHidden + ? dir->d_name[0] != '.' + : (name.compare(".") != 0 && name.compare("..") != 0) + ) { + string new_path(path); - new_path += string( "/" ) + string( dir->d_name ); + new_path += string( "/" ) + name; #if defined(WINDOWS) string new_system_path(system_path); - new_system_path += string( "\\" ) + string( dir->d_name ); + new_system_path += string( "\\" ) + name; #else string new_system_path( new_path ); #endif - print_node( output, base, new_system_path, new_path, uid, gid ); + print_node( output, base, new_system_path, new_path, uid, gid, includeHidden ); } } closedir( d ); } } -void print_node( ostream & output, string directory, uint32_t uid, uint32_t gid ) { +void print_node( ostream & output, string directory, uint32_t uid, uint32_t gid, bool includeHidden ) { if ( directory.size() < 1 ) { cerr << "Invalid path" << endl; exit(1); @@ -111,5 +116,5 @@ void print_node( ostream & output, string directory, uint32_t uid, uint32_t gid cout << endl << "Argument must be a directory" << endl; exit(1); } - print_node( output, directory, "", ".", uid, gid ); + print_node( output, directory, "", ".", uid, gid, includeHidden ); } diff --git a/src/printnode.hpp b/src/printnode.hpp index fc50c97..7e1d4ae 100644 --- a/src/printnode.hpp +++ b/src/printnode.hpp @@ -27,4 +27,4 @@ #include #include -void print_node( std::ostream & output, std::string directory, uint32_t uid, uint32_t gid ); +void print_node( std::ostream & output, std::string directory, uint32_t uid, uint32_t gid, bool includeHidden );