Migrate to normal project structure

This commit is contained in:
Bob Polis 2024-12-10 17:00:28 +01:00
parent 97c6131aca
commit c54020305d
12 changed files with 614 additions and 13 deletions

288
Makefile
View File

@ -1,31 +1,293 @@
LDLIBS := -lscstring
include premake.make
PROJ := $(shell basename $$(pwd))
# git commit hash and version for this build
COMMIT-HASH != git log 2>/dev/null | sed -e '1s/^commit //;q'
COMMIT := "const char* commit = \"$(COMMIT-HASH)\";"
VERSION := "const char* version = \"$(MAJOR).$(MINOR).$(PATCH)\";"
SRCS := $(wildcard *.cpp)
OBJS := $(SRCS:.cpp=.o)
DEPS := $(SRCS:.cpp=.dep)
# some important install locations
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
CONFIGDIR ?= $(PREFIX)/etc
INCLUDEDIR ?= $(PREFIX)/include
LIBDIR ?= $(PREFIX)/lib
MANDIR ?= $(PREFIX)/man
DATADIR ?= $(PREFIX)/share/$(PROJ)
DOCDIR ?= $(DATADIR)/doc
# setup naming and versioning for library product
ifeq ($(UNAME_S), Darwin)
LINKERNAME := $(PROJ).dylib
SONAME := $(PROJ).$(MAJOR).dylib
REALNAME := $(LINKERNAME)
else ifeq ($(UNAME_S), OpenBSD)
REALNAME := $(PROJ).so.$(MAJOR).$(MINOR)
else ifeq ($(UNAME_S), Linux)
LINKERNAME := $(PROJ).so
SONAME := $(LINKERNAME).$(MAJOR)
REALNAME := $(SONAME).$(MINOR).$(PATCH)
endif
STATICLIB := $(PROJ).a
# select default compiler
CXX ?= g++
# setup compiler flags and build config
CXXFLAGS += -Wshadow -Wall -Wpedantic -Wextra -Wno-unused-parameter
CXXFLAGS += -g3 -std=c++20
ifeq ($(DEBUG),1)
CXXFLAGS += -g3 -fPIC
ifeq ($(DEBUG), 1)
CXXFLAGS += -DDEBUG -O0
CONFIG := debug
else
CXXFLAGS += -DNDEBUG -O3
CONFIG := release
endif
%.o: %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -MT $@ -MF $*.dep -c $<
# setup build locations
OUTDIR := build/$(CONFIG)
BUILDDIR := build/obj
BIN := $(OUTDIR)/$(PROJ)
# define sources and derived files
# allow for extra sources defined in premake.make
SRCS += $(notdir $(wildcard src/*.cpp))
OBJS := $(addprefix $(BUILDDIR)/, $(SRCS:.cpp=.o))
DEPS := $(addprefix $(BUILDDIR)/, $(SRCS:.cpp=.dep))
HDRS ?= $(wildcard src/*.hpp)
MANS := $(addprefix $(PREFIX)/, $(wildcard man/man*/*))
# if project supports plugins, link to libdl where needed
ifdef PLUGINS
ifeq ($(UNAME_S),Darwin)
LDLIBS += -ldl
else ifeq ($(UNAME_S),Linux)
LDLIBS += -ldl
endif
endif
# pattern rules ===========================================================
$(BUILDDIR)/%.o: src/%.cpp
ifeq ($(PRECOMPILE), 1)
$(CXX) $(CXXFLAGS) -o $@ -include precomp.hpp -MMD -MP -MT $@ -MF $(BUILDDIR)/$*.dep -c $<
else
$(CXX) $(CXXFLAGS) -o $@ -MMD -MP -MT $@ -MF $(BUILDDIR)/$*.dep -c $<
endif
%.dep: ;
$(PROJ): $(OBJS)
$(CXX) $(LDFLAGS) -o $(PROJ) $(OBJS) $(LDLIBS)
$(MANDIR)/man1/%: man/man1/%
install -m 0644 $< $@
$(MANDIR)/man3/%: man/man3/%
install -m 0644 $< $@
$(MANDIR)/man5/%: man/man5/%
install -m 0644 $< $@
# =========================================================== pattern rules
# targets =================================================================
.PHONY: all commit-hash version plugins test clean dist-clean install-plugins \
install install-data uninstall-data uninstall-plugins uninstall
# main target
ifeq ($(PRODUCT), tool)
all: $(BUILDDIR) $(OUTDIR) commit-hash version plugins $(BIN)
else ifeq ($(PRODUCT), lib)
all: $(BUILDDIR) $(OUTDIR) commit-hash version plugins $(OUTDIR)/$(REALNAME) $(OUTDIR)/$(STATICLIB)
endif
$(BUILDDIR):
mkdir -p $@
$(OUTDIR):
mkdir -p $@
# product build rules
ifeq ($(PRODUCT), tool) # -------------------------------------------- tool
ifeq ($(PRECOMPILE), 1)
$(BIN): precomp.hpp.gch $(OBJS)
else
$(BIN): $(OBJS)
endif
$(CXX) -o $(BIN) $(LDFLAGS) $(OBJS) $(LDLIBS)
@ln -sf $(BIN) $(PROJ)
endif # -------------------------------------------------------------- tool
ifeq ($(PRODUCT), lib) # ---------------------------------------------- lib
ifeq ($(PRECOMPILE), 1)
$(OUTDIR)/$(REALNAME): precomp.hpp.gch $(OBJS)
else
$(OUTDIR)/$(REALNAME): $(OBJS)
endif
ifeq ($(UNAME_S),Darwin)
$(CXX) -dynamiclib -o $(OUTDIR)/$(REALNAME) -current_version $(MAJOR) -compatibility_version $(MINOR) $(LDFLAGS) $(OBJS) $(LDLIBS)
else ifeq ($(UNAME_S),OpenBSD)
$(CXX) -g -shared -Wl,-soname,$(REALNAME) -o $(OUTDIR)/$(REALNAME) $(LDFLAGS) $(OBJS) $(LDLIBS)
else ifeq ($(UNAME_S),Linux)
$(CXX) -g -shared -Wl,-soname,$(SONAME) -o $(OUTDIR)/$(REALNAME) $(LDFLAGS) $(OBJS) $(LDLIBS)
endif
$(OUTDIR)/$(STATICLIB): $(OBJS)
ar r $(OUTDIR)/$(STATICLIB) $(OBJS)
ifeq ($(GENERATELIBHEADER),1)
$(OUTDIR)/$(PROJ).hpp: $(HDRS)
@echo updating $(OUTDIR)/$(PROJ).hpp
@cp /dev/null $(OUTDIR)/$(PROJ).hpp
@for h in $(HDRS); \
do \
sed '/@exclude/d' $$h >> $(OUTDIR)/$(PROJ).hpp; \
done
endif
endif # --------------------------------------------------------------- lib
# get generated dependencies, if any
-include $(DEPS)
.PHONY: clean
commit-hash:
@if ! echo $(COMMIT) | diff -q src/commit.inc - >/dev/null 2>&1 ;\
then \
echo $(COMMIT) > src/commit.inc ;\
fi
version:
@if ! echo $(VERSION) | diff -q src/version.inc - >/dev/null 2>&1 ;\
then \
echo $(VERSION) > src/version.inc ;\
fi
$(BUILDDIR)/version.o: src/version.inc src/commit.inc
precomp.hpp.gch: src/precomp.hpp
$(CXX) $< $(CXXFLAGS) -o $@
plugins:
ifdef PLUGINS
@for plug in $(PLUGINS) ;\
do \
$(MAKE) -C $$plug ;\
done
endif
test:
$(MAKE) -C tests && tests/tests
clean:
rm -f $(OBJS) $(DEPS) $(PROJ)
rm -rf $(BUILDDIR) tests/build tests/tests tests/src/commit.inc tests/src/version.inc
ifdef PLUGINS
@for plug in $(PLUGINS) ;\
do \
$(MAKE) -C $$plug clean ;\
done
endif
dist-clean: clean
rm -rf build $(PROJ) src/commit.inc src/version.inc precomp.hpp.gch
$(MANDIR)/man1:
install -m 0755 -d $@
$(MANDIR)/man3:
install -m 0755 -d $@
$(MANDIR)/man5:
install -m 0755 -d $@
$(DATADIR):
install -m 0755 -d $@
# (un)install targets
ifdef DATAFILES
install-data: $(DATADIR)
install -m 0644 $(DATAFILES) $(DATADIR)
else
install-data:
endif
uninstall-data:
rm -rf $(DATADIR)
install-plugins:
ifdef PLUGINS
@for plug in $(PLUGINS) ;\
do \
$(MAKE) -C $$plug install ;\
done
endif
uninstall-plugins:
ifdef PLUGINS
@for plug in $(PLUGINS) ;\
do \
$(MAKE) -C $$plug uninstall ;\
done
endif
ifeq ($(PRODUCT), tool) # -------------------------------------------- tool
$(BINDIR):
install -m 0755 -d $@
install: $(MANDIR)/man1 $(MANDIR)/man3 $(MANDIR)/man5 $(BINDIR) $(MANS) install-data install-plugins
install -m 0755 $(BIN) $(BINDIR)
uninstall: uninstall-data uninstall-plugins
rm -f $(BINDIR)/$(PROJ) $(MANS)
endif # -------------------------------------------------------------- tool
ifeq ($(PRODUCT), lib) # ---------------------------------------------- lib
$(LIBDIR):
install -m 0755 -d $@
$(INCLUDEDIR):
install -m 0755 -d $@
$(INCLUDEDIR)/$(PROJ):
install -m 0755 -d $@
ifeq ($(GENERATELIBHEADER), 1)
install: $(MANDIR)/man1 $(MANDIR)/man3 $(MANDIR)/man5 $(LIBDIR) $(INCLUDEDIR) $(MANS) $(OUTDIR)/$(PROJ).hpp install-data install-plugins
install -m 0644 $(OUTDIR)/$(PROJ).hpp $(INCLUDEDIR)
else
install: $(MANDIR)/man1 $(MANDIR)/man3 $(MANDIR)/man5 $(LIBDIR) $(INCLUDEDIR) $(MANS) $(INCLUDEDIR)/$(PROJ) install-data install-plugins
install -m 0644 $(HDRS) $(INCLUDEDIR)/$(PROJ)
endif
install -m 0644 $(OUTDIR)/$(REALNAME) $(LIBDIR)
install -m 0644 $(OUTDIR)/$(STATICLIB) $(LIBDIR)
ifeq ($(UNAME_S), Darwin)
cd $(LIBDIR) && ln -sf $(REALNAME) $(SONAME)
else ifeq ($(UNAME_S), Linux)
ifeq ($(USER), root)
ldconfig
else
cd $(LIBDIR) && ln -sf $(REALNAME) $(SONAME)
endif
cd $(LIBDIR) && ln -sf $(SONAME) $(LINKERNAME)
else ifeq ($(UNAME_S), OpenBSD)
ldconfig -R
endif
uninstall: uninstall-data uninstall-plugins
rm -rf $(INCLUDEDIR)/$(PROJ).hpp $(INCLUDEDIR)/$(PROJ) $(MANS) $(LIBDIR)/$(STATICLIB) $(LIBDIR)/$(REALNAME) $(LIBDIR)/$(SONAME) $(LIBDIR)/$(LINKERNAME)
ifeq ($(UNAME_S), Linux)
ldconfig
else ifeq ($(UNAME_S), OpenBSD)
ldconfig -R
endif
endif # --------------------------------------------------------------- lib
# ================================================================= targets
# project-specific targets go in here
-include postmake.make

59
man/man1/valid-utf8.1 Normal file
View File

@ -0,0 +1,59 @@
.Dd $Mdocdate$
.Dt valid-utf8 1
.Os
.Sh NAME
.Nm valid-utf8
.Nd one line about what it does
.\" .Sh LIBRARY
.\" For sections 2, 3, and 9 only.
.\" Not used in OpenBSD.
.Sh SYNOPSIS
.Nm
.Fl h
.Nm
.Fl \-version
.Nm
.Op Ar
.Sh DESCRIPTION
The
.Nm
utility processes files ...
When no file arguments are given,
.Nm
will read from the standard input.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl h, \-help
Print help text and exit.
.It Fl \-version
Print version info and exit.
.El
.\" .Sh CONTEXT
.\" For section 9 functions only.
.\" .Sh IMPLEMENTATION NOTES
.\" Not used in OpenBSD.
.\" .Sh RETURN VALUES
.\" For sections 2, 3, and 9 function return values only.
.\" .Sh ENVIRONMENT
.\" For sections 1, 6, 7, and 8 only.
.\" .Sh FILES
.Sh EXIT STATUS
.\" For sections 1, 6, and 8 only.
.Nm
exits 0 on success, and 1 if an error occurs.
.\" .Sh EXAMPLES
.\" .Sh DIAGNOSTICS
.\" For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
.\" .Sh ERRORS
.\" For sections 2, 3, 4, and 9 errno settings only.
.\" .Sh SEE ALSO
.\" .Xr foobar 1
.\" .Sh STANDARDS
.\" .Sh HISTORY
.Sh AUTHORS
Bob Polis
.\" .Sh CAVEATS
.\" .Sh BUGS
.\" .Sh SECURITY CONSIDERATIONS
.\" Not used in OpenBSD.

31
premake.make Normal file
View File

@ -0,0 +1,31 @@
# Define linker flags here, like: -lsqlite3 -lpthread
LDLIBS := -lscstring
# Dir name becomes product name.
PROJ := $(shell basename $$(pwd))
# Find out what platform we're on, e.g. Darwin, Linux, OpenBSD.
UNAME_S := $(shell uname -s)
# We will build a tool, not a library.
PRODUCT := tool
# Single source of truth for version.
MAJOR := 1
MINOR := 0
PATCH := 0
# Specify desired C++ standard for this project.
CXXFLAGS += -std=c++20
# Set to 1 if you want a precompiled header for the C++ Standard Library.
PRECOMPILE := 0
# List of extra files to be installed in DATADIR (/usr/local/share/$(PROJ) by default)
DATAFILES :=
# Define plugin sub-projects here. Assumption here is a directory "plugins"
# containing plugin sub-projects, each in its own directory.
# Rename and/or repeat accordingly.
PLUGINS := $(wildcard plugins/*)
MAKE += --no-print-directory

74
src/main.cpp Normal file
View File

@ -0,0 +1,74 @@
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <stdexcept>
#include <fstream>
#include <getopt.h>
#include <libscstring.hpp>
#include "version.hpp"
static void print_help() {
std::cout << "usage: valid-utf8 [-h]\n";
std::cout << " -h, --help show this help text and exit\n";
std::cout << " --version show version number and exit\n";
}
static int check_file(std::istream& file) {
std::string line;
while (std::getline(file, line)) {
if (!sc::is_valid_utf8(line)) return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main(int argc, char * argv[]) {
try {
int opt_char, opt_val;
struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{"version", no_argument, &opt_val, 1},
{nullptr, 0, nullptr, 0}
};
while ((opt_char = getopt_long(argc, argv, "h", long_options, nullptr)) != -1) {
switch (opt_char) {
case 0: {
// handle long-only options here
switch (opt_val) {
case 1:
std::cout << valid_utf8_version() << std::endl;
return EXIT_SUCCESS;
}
break;
}
case 'h':
print_help();
return EXIT_SUCCESS;
case '?':
throw std::runtime_error("unrecognized option");
}
}
if (optind == argc) {
return check_file(std::cin);
} else if (optind == argc - 1) {
std::ifstream file {argv[optind]};
if (!file) throw std::runtime_error {"could not open file"};
return check_file(file);
}
for (int i = optind; i < argc; ++i) {
try {
// process file argv[i]
std::ifstream file {argv[i]};
if (!file) throw std::runtime_error {std::string {"could not open file: "} + argv[i]};
int ret = check_file(file);
if (ret == EXIT_FAILURE) throw std::runtime_error {std::string {"not valid utf8: "} + argv[i]};
} catch (const std::runtime_error& ex) {
std::cerr << "valid-utf8: " << ex.what() << '\n';
}
}
} catch (const std::exception& ex) {
std::cerr << "valid-utf8: " << ex.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

127
src/precomp.hpp Normal file
View File

@ -0,0 +1,127 @@
// C++98 (first official C++ standard)
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwchar>
#include <cwctype>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#if (__cplusplus >= 201103L) // C++11
#include <array>
#include <atomic>
#include <cfenv>
#include <chrono>
#include <cinttypes>
#include <codecvt> // deprecated in C++17, removed in C++26
#include <condition_variable>
#include <cstdint>
#include <cuchar>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <unordered_map>
#include <unordered_set>
#endif // C++11
#if (__cplusplus >= 201402L) // C++14
#include <shared_mutex>
#endif // C++14
#if (__cplusplus >= 201703L) // C++17
#include <any>
#include <charconv>
#include <execution>
#include <filesystem>
#include <memory_resource>
#include <optional>
#include <string_view>
#include <variant>
#endif // C++17
#if (__cplusplus >= 202002L) // C++20
#include <barrier>
#include <bit>
#include <compare>
#include <concepts>
#include <coroutine>
#include <format>
#include <latch>
#include <numbers>
#include <ranges>
#include <semaphore>
#include <source_location>
#include <span>
//#include <stop_token> not yet supported by clang 16
//#include <syncstream> not yet supported by clang 17
#include <version>
#endif // C++20
#if (__cplusplus >= 202302L) // C++23
#include <expected>
#include <flat_map>
#include <flat_set>
#include <generator>
#include <mdspan>
#include <print>
#include <spanstream>
#include <stacktrace>
#include <stdfloat>
#endif // C++23
#if (__cplusplus > 202302L) // C++26
#include <debugging>
#include <hazard_pointer>
#include <inplace_vector>
#include <linalg>
#include <rcu>
#include <text_encoding>
#endif // C++26

19
src/version.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "version.hpp"
#include "version.inc"
#include "commit.inc"
#include <sstream>
std::string valid_utf8_version() {
std::ostringstream oss;
oss << "valid-utf8 version " << version;
#ifdef DEBUG
oss << " DEBUG";
#endif
oss << '\n';
if (commit[0] != '\0') {
oss << "build " << commit << ", ";
}
oss << __DATE__ << ", " << __TIME__ << '\n';
oss << "(c) Bob Polis, all rights reserved";
return oss.str();
}

8
src/version.hpp Normal file
View File

@ -0,0 +1,8 @@
#ifndef VERSION_H_
#define VERSION_H_
#include <string>
std::string valid_utf8_version();
#endif // VERSION_H_

1
tests/Makefile Symbolic link
View File

@ -0,0 +1 @@
../Makefile

6
tests/postmake.make Normal file
View File

@ -0,0 +1,6 @@
$(BUILDDIR)/%.o: ../src/%.cpp
ifeq ($(PRECOMPILE), 1)
$(CXX) $(CXXFLAGS) -o $@ -include precomp.hpp -MMD -MP -MT $@ -MF $(BUILDDIR)/$*.dep -c $<
else
$(CXX) $(CXXFLAGS) -o $@ -MMD -MP -MT $@ -MF $(BUILDDIR)/$*.dep -c $<
endif

5
tests/premake.make Normal file
View File

@ -0,0 +1,5 @@
include ../premake.make
LDLIBS += -lboost_unit_test_framework
CXXFLAGS += -I../src
SRCS := $(notdir $(filter-out ../src/main.cpp,$(wildcard ../src/*.cpp)))
PRODUCT := tool

8
tests/src/main.cpp Normal file
View File

@ -0,0 +1,8 @@
#define BOOST_TEST_MODULE My Test
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(first_test)
{
BOOST_TEST(1 == 1);
}

1
tests/src/precomp.hpp Symbolic link
View File

@ -0,0 +1 @@
../../src/precomp.hpp