Compare commits

..

39 Commits

Author SHA1 Message Date
e73fa39be1 Simplify simple project even more 2025-05-01 09:48:24 +02:00
1e32445243 Fix author markup 2025-04-10 06:34:35 +02:00
9aaf7bccc1 Bump version 2.0.0 -> 2.0.1 2025-02-28 09:31:32 +01:00
7fa2dce9e5 Simplify boolean flag handling 2025-02-28 09:28:26 +01:00
63d2a2d0e2 Add main project's src dir to header search path 2025-01-05 16:07:21 +01:00
acd78e092f Add optional postmake.make include at end 2025-01-05 15:28:28 +01:00
4480c0a690 Fix stale symlink copying 2024-12-31 16:07:43 +01:00
05bf0700b5 Make sure no symlinks are followed when copying 2024-12-31 16:02:17 +01:00
2d551c59e2 Change date, more refinements 2024-12-31 14:45:50 +01:00
f3c40f4bd7 Add -n option, and other improvements 2024-12-31 14:34:36 +01:00
63dd34e926 Add -n option for normal project type
For consistency, I added this option, so any project type can be
explicitly specified. Of course, this option is the default, so it can
be left out to yield the existing behaviour.
2024-12-31 14:32:05 +01:00
9a754c83fa Changed option handling to getopts shell-builtin
The long option support, made available through the Linux version of
getopt, is incompatible with BSD getopt, found on macOS and OpenBSD.
So I decided to keep it simple and support single-letter options only.
2024-12-31 13:55:23 +01:00
c83be332d5 Fix version and commit display
Made version and commit variables static, and local to the version
display function. Previously, they would be globals, and as such would
collide with those from linked shared libraries. Now, all project types
will have them local.
2024-12-31 11:58:35 +01:00
178b2f4860 Fix library header generation
Prevent precomp.hpp to be included in header file list.
2024-12-31 11:07:27 +01:00
64b81076c9 Make include guard in version.hpp uppercase 2024-12-31 10:52:18 +01:00
27f42b1623 Fix runtime library loading for macOS 2024-12-30 13:19:55 +01:00
2e53bc0dc5 Use ASCII drawing for tree output 2024-12-30 13:17:06 +01:00
99c951c2f8 Fix typo 2024-12-29 15:56:38 +01:00
d5106f796a Add FILES section, fix quoting 2024-12-27 10:21:21 +01:00
1c2e2fd087 Change name 2024-12-27 10:03:47 +01:00
a204a6b5f6 Remove note about incomplete project 2024-12-27 10:03:04 +01:00
9c1485824f Add man page 2024-12-27 10:02:39 +01:00
5cce3fc560 Add mkproj.sh 2024-12-26 11:56:42 +01:00
3e3b103e7d Quote data dir in config file 2024-12-26 10:04:49 +01:00
57347934d4 Change config file to ini format 2024-12-26 10:03:12 +01:00
fc966fb62f Improve installation of symlinks 2024-12-26 08:51:28 +01:00
546a7deb42 Quote DATADIR 2024-12-24 11:19:10 +01:00
c43c14fb19 Add Makefile which installs everything 2024-12-24 11:04:48 +01:00
111ad3c2be Explain about the install location of templates 2024-12-24 11:04:23 +01:00
2e66524e2c Delete symlinks, will be created by Makefile 2024-12-24 10:55:42 +01:00
9e147ba41e Add make test explanation 2024-12-24 10:40:34 +01:00
9fadf2548b Small clarifications 2024-12-24 10:38:06 +01:00
1517c01832 Fix errors 2024-12-24 10:25:37 +01:00
b80b730a39 Add note 2024-12-23 17:14:52 +01:00
c2a5b0cb2f Fix installation recipe 2024-12-23 16:33:08 +01:00
e55c019ef0 Add template files 2024-12-23 16:18:39 +01:00
d2b64fdc5a Change mkcppproj to mkproj
Other project types can and will be possible in the future.
2024-12-23 16:14:51 +01:00
93f11efcc1 Add content to README.md 2024-12-23 15:55:44 +01:00
1f64049823 Fix name 2024-12-23 15:55:35 +01:00
23 changed files with 1467 additions and 3 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 bob
Copyright (c) 2025 Bob Polis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

72
Makefile Normal file
View File

@ -0,0 +1,72 @@
# Override PREFIX for another root installation directory
PREFIX ?= $(HOME)/.local
BINDIR := $(PREFIX)/bin
MANDIR := $(PREFIX)/man/man1
DATADIR := $(PREFIX)/share/mkproj
TESTSDIR := $(DATADIR)/tests
TESTSSRCDIR := $(TESTSDIR)/src
TPLS := $(addprefix $(DATADIR)/, $(notdir $(wildcard tpl/*)))
TESTFILES := $(addprefix $(TESTSDIR)/, $(notdir $(wildcard tpl/tests/*)))
TESTSRCFILES := $(addprefix $(TESTSSRCDIR)/, $(notdir $(wildcard tpl/tests/src/*)))
# Pattern rules
$(DATADIR)/%: tpl/%
install -m 0644 $< $@
$(TESTSDIR)/%: tpl/tests/%
install -m 0644 $< $@
$(TESTSSRCDIR)/%: tpl/tests/src/%
install -m 0644 $< $@
# Targets
.PHONY: all
all: \
$(BINDIR) \
$(MANDIR) \
$(DATADIR) \
$(TESTSDIR) \
$(TESTSSRCDIR) \
$(BINDIR)/mkproj \
$(MANDIR)/mkproj.1 \
$(TPLS) \
$(TESTFILES) \
$(TESTSDIR)/Makefile \
$(TESTSRCFILES) \
$(TESTSSRCDIR)/src/precomp.hpp \
$(HOME)/.config/mkprojrc
$(BINDIR):
mkdir -p $@
@printf "\e[33m%s\n\e[m" "Please make sure that $(BINDIR) is in your path."
$(BINDIR)/mkproj: mkproj.sh
install -m 0755 $< $@
$(MANDIR):
mkdir -p $@
$(MANDIR)/mkproj.1: mkproj.1
install -m 0644 $< $@
$(DATADIR):
mkdir -p $@
$(TESTSDIR):
mkdir -p $@
$(TESTSDIR)/Makefile:
cd $(TESTSDIR) && ln -fs ../Makefile Makefile
$(TESTSSRCDIR):
mkdir -p $@
$(TESTSSRCDIR)/src/precomp.hpp:
cd $(TESTSSRCDIR) && ln -fs ../../src/precomp.hpp precomp.hpp
$(HOME)/.config/mkprojrc:
echo "tplroot=\"$(DATADIR)\"" > $@

View File

@ -1,3 +1,59 @@
# proj_tpl
# mkproj
Generic project templates for tools, filters, libraries in C++.
Generic project templates for tools, filters, libraries in C++.
## Installation
Type `make` to install the `mkproj` script, its man page, and the templates.
The default location is to put everything inside your `~/.local` hierarchy, but
you can change that by defining an environment variable `PREFIX`, giving it your
desired root directory as a value, before typing `make`.
The actual location used will be recorded in a config file:
`~/.config/mkprojrc`, which will be read by the `mkproj` script. It needs to
know where your templates are in order to function.
## Overview
With `mkproj`, you can create a C++ project for a tool or a library.
The project can be:
- a simple tool, to test some idea;
- a more elaborate tool, meant to be distributed;
- a UNIX filter, which processes input files line by line, or reads from
standard input when there are no input files;
- a library, where both a static and a dynamic library will be built.
Apart from the simple tool, all of these can optionally:
- use a precompiled header for the C++ Standard Library;
- implement a plugin architecture, where building and installing plugins is
automatically supported.
At the center is a `Makefile`, which should not be altered. This `Makefile` can
build and install any of these projects. All project-specific additions should
be put in the mandatory `premake.make` (which is included at the start of the
Makefile), and in an optional `postmake.make` (which is included at the end of
the Makefile).
The `premake.make` typically defines some Make variables, whereas the
`postmake.make` typically contains extra Make targets.
The `premake.make` is mandatory, because it defines the *name* of the tool or
library, and its *version*. This is also where you declare external
dependencies.
Available Make targets for your project are:
- `make` will build the tool or library;
- `make test` will build and run tests, using the Boost Unit Test Framework;
- `sudo make install` will install it, and all of its accompanying files;
- `make clean` will remove compiled object files, but will keep a previously
built tool or library, and the precompiled header;
- `make dist-clean` will bring the project to a pristine state.
- `sudo make uninstall` will remove all installed files.
## Documentation
`man mkproj`

340
mkproj.1 Normal file
View File

@ -0,0 +1,340 @@
.Dd December 31, 2024
.Dt mkproj 1
.Os
.Sh NAME
.Nm mkproj
.Nd Create C++ project directory with predefined templates
.Sh SYNOPSIS
.Nm
.Fl v
.Nm
.Op Fl f | Fl l | Fl n | Fl s
.Oo
.Op Fl d
.Fl g Ar gitroot
.Oc
.Op Fl t Ar tplroot
.Ar project
.Sh DESCRIPTION
The
.Nm
script creates a C++ project directory structure with some predefined
boilerplate files, ready to be built using
.Xr make 1 .
.Pp
Please note that you will need GNU Make to use the supplied Makefiles, which is
standard on Linux systems.
On BSD systems, use
.Xr gmake 1 ,
which is usually available through a package manager for the platform.
.Pp
.Nm
can create a directory for a simple project using a single source file; a
general project suitable for many source files; a general project setup as a
standard UNIX filter; and a library project, setup to create a static as well as
a shared library.
.Pp
It will also setup the project for use with
.Xr git 1 ,
and do a first commit for the project's files.
.Pp
The main.cpp source files for the general and filter projects already have code
in place to do complete option handling, for long and short options. It can also
display a version string, containing the
.Xr git 1
commit hash, after you have built the tool at least once.
.Pp
The options for
.Nm
are as follows:
.Ss General Options
.Bl -tag -width Ds
.It Fl d
Save default Git URL to clone from.
Will only have any effect when also using the
.Fl g
option.
It will cause the
.Ar gitroot
argument for
.Fl g
to be saved into the
.Nm
configuration file, for later use with the
.Fl G
option.
.It Fl g Ar gitroot
Clone a
.Xr git 1
repo from the supplied git URL.
When combined with the
.Fl d
option, the supplied git URL will be saved into the
.Nm
configuration file.
.It Fl G
Use the Git URL saved earlier to clone a repo from.
.It Fl t Ar tplroot
Specify the root directory where the project templates can be found.
This option is useful when you want to use a modified set of files to base your
projects on.
.It Fl v
Print version info and exit.
.El
.Ss Project Type Options
.Bl -tag -width Ds
.It Fl f
Create a filter tool project.
.It Fl l
Create a library project.
.It Fl n
Create a normal tool project.
This is the default.
.It Fl s
Create a simple tool project.
.El
.Pp
Without any
.Sx Project Type Options ,
A general project for a command line tool with multiple source files will be
created.
.Pp
The
.Sx Project Type Options
are mutually exclusive.
.\" .Sh ENVIRONMENT
.\" For sections 1, 6, 7, and 8 only.
.Sh FILES
.Bl -tag -width Ds
.It Pa ~/.config/mkprojrc
This file is sourced by the
.Nm
script, and will at least contain the definition for the
.Sq tplroot
variable, and possibly also the definition of the
.Sq gitroot
variable.
Since it is shell syntax, paths should be properly quoted, and comments are
allowed.
.El
.Sh EXIT STATUS
.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 PROJECT DESCRIPTION
The project created by
.Nm
has the following features.
.Ss Normal Tool Project
This creates a full-featured project for a tool, with the following layout:
.Bd -literal
mytool
|-- Makefile
|-- man
| `-- man1
| `-- mytool.1
|-- premake.make
|-- src
| |-- main.cpp
| |-- precomp.hpp
| |-- version.cpp
| `-- version.hpp
`-- tests
|-- Makefile -> ../Makefile
|-- postmake.make
|-- premake.make
`-- src
|-- main.cpp
`-- precomp.hpp -> ../../src/precomp.hpp
.Ed
.Pp
In this example, the project name is
.Dq mytool .
.Pp
Description:
.Bl -tag -width Ds
.It Makefile
This is a universal Makefile, which should not be edited.
All customization should be done in premake.make, and, optionally,
postmake.make.
The idea is that you should be able to replace it, at any time, with a newer
version, without affecting the overall functionality.
See
.Sx Makefile targets
below for more details about what's possible here.
.It man directories
Contains a boilerplate man page, ready to be edited with relevant content.
If the project needs multiple man pages, possibly also for man chapter 3 and/or
5, they can be added here.
The structure will be merged into a man directory hierarchy at install time.
.It premake.make
This file will be included by the Makefile, as a first action.
In it, several important project-related Makefile variables can and will be
defined.
.Bl -tag -width Ds
.It LDLIBS
Initially empty, this is where you can specify library dependencies, like
-lsqlite3, or -lpthread.
.It PROJ
By default, this will evaluate to the project directory's name.
Alter only if you really want another name for the tool you'll build.
Defined here, so you can use it inside the premake.make, should you need to
derive other variables from it.
.It UNAME_S
Should not be changed; here as a convenience to be able to conditionally define
Makefile rules, dependent on the type of operating system.
Defined here, so you can use it inside the premake.make.
.It PRODUCT
Only two values allowed:
.Dq tool ,
or
.Dq lib .
Used by the Makefile to do the right thing for either type of project.
.It MAJOR, MINOR, PATCH
Together, they form the version for your project.
Important: this is the single source of truth for the version numbering, and
will be used in the Makefile as well as in the preinstalled sources.
.It CXXFLAGS
This is where the C++ standard is defined.
Change if needed for your project.
Note the
.Dq +=
operator here, so a possibly defined
.Ev CXXFLAGS
environment variable will be honored.
.It PRECOMPILE
The project can generate and use a precompiled header.
By default, the precomp.hpp file contains the C++ Standard Library headers, but
you could add more to that file, if you like (for example, other library
headers).
By default, it is switched off.
Set to 1 if you want to switch it on.
.It PLUGINS
If your project supports plugins of any type, define the directory name(s) here.
The Makefile will then automatically build and install plugins as well.
Leave empty if your project does not use any plugins.
.El
.It src directory
Subdirectory for headers and source files.
The file already present can be used to compile and build the tool, providing
some nice features out of the box.
See
.Sx Tool Features
below for more information.
.It tests directory
Subdirectory for unit tests, using the Boost Unit Test Framework.
.El
.Ss Filter Tool Project
The filter tool project has an identical structure as the normal type, but adds
some functionality to make the tool behave as a real UNIX filter.
See
.Sx Tool Features
below for more information.
.Ss Library Project
The library project has been set up to produce a static and dynamic library from
your sources.
Its directory structure is almost identical to that of a tool.
The only differences are, that the man page is for man chapter 3, and there is
no main.cpp source file.
.Pp
When building the library, a static and dynamic library are produced, containing
by default a single function which can return a version string.
.Pp
TODO describe lib header generation, honoring @exclude
.Ss Simple Tool Project
The simple tool project is just a directory with a single source file and a
Makefile.
The idea is that you use this for simple, quick proof-of-concept tools.
Here, the source file has the same name as the project, and is almost empty, so
nothing is in the way to make it do what you want.
The Makefile is very simple, can do a
.Dq make
as well as a
.Dq make clean ,
but no more.
Possible library dependencies should be added to the Makefile.
.Ss Makefile targets
The description below is valid for all project types, except the simple tool
project.
.Bl -tag -width Ds
.It make
Use this to compile and link your tool.
When doing this for the first time, or after doing a
.Dq make dist-clean ,
a
.Dq build
subdirectory will be created.
Inside it, an
.Dq obj
subdirectory appears, which will contain all object files
(*.o), and all dependency files (*.dep), used by the Makefile.
Also, either a
.Dq debug
or
.Dq release
subdirectory will appear, depending on the
definition of the
.Ev DEBUG
environment variable.
This is the place where your tool or libraries will appear.
For convenience, a symlink is created on the top level of your project, pointing
to the executable, if it's a tool.
That way, you can simply do
.Ql ./mytool
to run it.
.It make test
This will build and run a test tool from the tests directory.
You are expected to add your tests to the tests/src/main.cpp file, using
assertions from the Boost Unit Test Framework.
Running tests will report successes and failures.
.It make install
When installing your tool and its man page outside your home directory, prefix
with
.Dq sudo ,
or
.Dq doas ,
depending on your system.
.It make clean
This will remove compiled object files and dependency files, but keep the
previously built tool and precompiled header intact.
.It make dist-clean
This brings the project directory back to a pristine state, removing everything
that is generated using the various
.Dq make
invocations.
.It make uninstall
Remove the files that are installed by the
.Dq make install
invocation.
Please note that no dependency checks are done, so removal could break other
tools or libraries that depend on the removed files.
.Pp
Also, if the install also created standard directories, like
.Pa /usr/local/bin ,
.Pa /usr/local/lib ,
.Pa /usr/local/include ,
etc., and they end up empty after the uninstall, they won't be removed.
.El
.Ss Tool Features, out of the box
TODO describe main.cpp and version.cpp here.
.Sh SEE ALSO
.Xr make 1 ,
.Xr git 1 .
.\" .Xr foobar 1
.\" .Sh STANDARDS
.\" .Sh HISTORY
.Sh AUTHORS
.An Bob Polis
.\" .Sh CAVEATS
.Sh BUGS
.Nm
does no error checking for using multiple
.Sx Project Type Options
at once.
You are at the mercy of the script which type will be created in that case.
.\" .Sh SECURITY CONSIDERATIONS
.\" Not used in OpenBSD.

138
mkproj.sh Normal file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env bash
config=$HOME/.config/mkprojrc
source $config
default=false
mainfile=main.cpp
gitfav=false
library=false
simple=false
usegitroot=false
usage() {
echo "usage: $progname [-f|-l|-n|-s] [[-d] -g <gitroot>|-G] [-t <tplroot>] PROJECTNAME" >&2
exit 1
}
msg() {
echo "$progname:" "$@" >&2
}
die() {
msg "$@"
exit 1
}
check_gitroot() {
[ -n "$gitroot" ] || die "please use --gitroot to specify a git url, used as prefix for all your repos"
}
progname=${0##*/}
# option processing, using silent mode
while getopts :dfGg:lnst:v opt
do
case $opt in
d)
default=true
;;
f)
mainfile=filter.cpp
;;
G)
gitfav=true
;;
g)
gitroot=$OPTARG
usegitroot=true
;;
l)
library=true
;;
n)
mainfile=main.cpp
library=false
simple=false
;;
s)
simple=true
;;
t)
tplroot=$OPTARG
;;
v)
echo "$progname version 2.0.1"
exit 0
;;
\?)
msg "invalid option -$OPTARG"
usage
;;
:)
msg "option -$OPTARG needs argument"
usage
;;
esac
done
# leave only file arguments
shift $(( OPTIND - 1 ))
[ $# -gt 0 ] || usage
[ -n "$tplroot" ] || die "no template root directory defined"
proj=${1//[- ]/_} # replace all spaces and dashes with underscores
if $default
then
check_gitroot
if grep -q gitroot $config
then
sed -i -e "s/^gitroot=.*$/gitroot=\"$gitroot\"" $config
else
printf 'gitroot="%s"\n' $gitroot >> $config
fi
fi
if $gitfav || $usegitroot
then
check_gitroot
git clone "$gitroot/$proj.git" || die "not an existing repo: $proj"
else
mkdir -p "$proj"
fi
if $simple
then
sed -e "s/{PROJECT}/$proj/g" $tplroot/Makefile.simple > $proj/Makefile
cp $tplroot/main.simple.cpp $proj/$proj.cpp
else
mkdir -p $proj/src
cp $tplroot/Makefile.unified $proj/Makefile
cp $tplroot/precomp.hpp $proj/src
cp -a $tplroot/tests $proj/
uproj=$(echo $proj | tr '[:lower:]' '[:upper:]')
sed -e "s/{PROJECT}/$proj/" -e "s/{PROJ}/$uproj/" $tplroot/version.hpp > $proj/src/version.hpp
sed -e "s/{PROJECT}/$proj/" $tplroot/version.cpp > $proj/src/version.cpp
if $library
then
mkdir -p $proj/man/man3
sed -e "s/{PROJECT}/$proj/" $tplroot/lib.3 > $proj/man/man3/$proj.3
cp $tplroot/premake.lib.make $proj/premake.make
else
mkdir -p $proj/man/man1
sed -e "s/{PROJECT}/$proj/" $tplroot/tool.1 > $proj/man/man1/$proj.1
cp $tplroot/premake.tool.make $proj/premake.make
sed -e "s/{PROJECT}/$proj/" $tplroot/$mainfile > $proj/src/main.cpp
fi
fi
sed -e "s/{PROJECT}/$proj/" $tplroot/gitignore > $proj/.gitignore
if [ ! -d .git ]
then
cd $proj
git init
git add ./* .gitignore
git commit -m 'First commit'
fi

53
tpl/Makefile.plugin Normal file
View File

@ -0,0 +1,53 @@
include premake.make
# some important install locations
PREFIX ?= /usr/local
DATADIR ?= $(PREFIX)/share
INSTALLDIR ?= $(DATADIR)/$(INSTALLDIRNAME)
SRCS := $(wildcard *.cpp)
OBJS := $(SRCS:.cpp=.o)
DEPS := $(SRCS:.cpp=.dep)
CXX ?= g++
CXXFLAGS += -Wshadow -Wall -Wpedantic -Wextra -Wno-unused-parameter
CXXFLAGS += -g3 -fPIC
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -O0
else
CXXFLAGS += -DNDEBUG -O3
endif
%.o: %.cpp
$(CXX) $(CXXFLAGS) -o $@ -MMD -MP -MT $@ -MF $*.dep -c $<
%.dep: ;
.PHONY: clean install uninstall
$(PLUGIN): $(OBJS)
ifeq ($(UNAME_S),Darwin)
$(CXX) -g3 -dynamiclib -o $(PLUGIN) $(LDFLAGS) $(OBJS) $(LDLIBS)
else
$(CXX) -g3 -shared -o $(PLUGIN) $(LDFLAGS) $(OBJS) $(LDLIBS)
endif
-include $(DEPS)
clean:
rm -f $(OBJS) $(DEPS) $(PLUGIN)
$(INSTALLDIR):
install -m 0755 -d $@
install: $(INSTALLDIR)
install -m 0644 $(PLUGIN) $(INSTALLDIR)
ifdef EXTRAFILES
install $(EXTRAFILES) $(INSTALLDIR)
endif
uninstall:
rm -f $(INSTALLDIR)/$(PLUGIN) $(addprefix $(INSTALLDIR)/, $(EXTRAFILES))
-include postmake.make

18
tpl/Makefile.simple Normal file
View File

@ -0,0 +1,18 @@
CXXFLAGS += -Wshadow -Wall -Wpedantic -Wextra -Wno-unused-parameter
CXXFLAGS += -g3 -std=c++20
ifeq ($(DEBUG), 1)
CXXFLAGS += -DDEBUG -O0
else
CXXFLAGS += -DNDEBUG -O3
endif
.PHONY: clean
%.o: %.cpp
c++ $(CXXFLAGS) -c $<
{PROJECT}: {PROJECT}.o
c++ -o {PROJECT} {PROJECT}.o
clean:
rm -f {PROJECT}.o {PROJECT}

294
tpl/Makefile.unified Normal file
View File

@ -0,0 +1,294 @@
include premake.make
# git commit hash and version for this build
COMMIT-HASH != git log 2>/dev/null | sed -e '1s/^commit //;q'
COMMIT := "static const char* commit = \"$(COMMIT-HASH)\";"
VERSION := "static const char* version = \"$(MAJOR).$(MINOR).$(PATCH)\";"
# 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 -fPIC
ifeq ($(DEBUG), 1)
CXXFLAGS += -DDEBUG -O0
CONFIG := debug
else
CXXFLAGS += -DNDEBUG -O3
CONFIG := release
endif
# 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 ?= $(filter-out src/precomp.hpp, $(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: ;
$(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) -install_name $(LIBDIR)/$(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)
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 -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

73
tpl/filter.cpp Normal file
View File

@ -0,0 +1,73 @@
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <stdexcept>
#include <fstream>
#include <getopt.h>
#include "version.hpp"
void print_help() {
std::cout << "usage: {PROJECT} [-h]\n";
std::cout << " -h, --help show this help text and exit\n";
std::cout << " --version show version number and exit\n";
}
std::string filter(const std::string& text) {
std::string result;
// your filter code here
result = text; // null-filter
return result;
}
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 << {PROJECT}_version() << std::endl;
return EXIT_SUCCESS;
}
break;
}
case 'h':
print_help();
return EXIT_SUCCESS;
case '?':
throw std::runtime_error("unrecognized option");
}
}
if (optind == argc) {
// no file args => read lines from stdin
std::string line;
while (getline(std::cin, line)) {
std::cout << filter(line) << '\n';
}
}
for (int i = optind; i < argc; ++i) {
try {
// process file argv[i]
std::ifstream file {argv[i]};
std::string line;
while (getline(file, line)) {
std::cout << filter(line) << '\n';
}
} catch (const std::runtime_error& ex) {
std::cerr << "{PROJECT}: " << ex.what() << '\n';
}
}
} catch (const std::exception& ex) {
std::cerr << "{PROJECT}: " << ex.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

2
tpl/gitignore Normal file
View File

@ -0,0 +1,2 @@
*.inc
{PROJECT}

30
tpl/lib.3 Normal file
View File

@ -0,0 +1,30 @@
.Dd $Mdocdate$
.Dt {PROJECT} 3
.Os
.Sh NAME
.Nm {PROJECT}
.Nd one line about what it does
.\" .Sh LIBRARY
.\" For sections 2, 3, and 9 only.
.\" Not used in OpenBSD.
.Sh DESCRIPTION
The
.Nm
library ...
.\" .Sh RETURN VALUES
.\" For sections 2, 3, and 9 function return values only.
.\" .Sh ENVIRONMENT
.\" .Sh FILES
.\" .Sh EXAMPLES
.\" .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.

57
tpl/main.cpp Normal file
View File

@ -0,0 +1,57 @@
#include <iostream>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <getopt.h>
#include "version.hpp"
void print_help() {
std::cout << "usage: {PROJECT} [-h|--version]\n";
std::cout << " -h, --help show this help text and exit\n";
std::cout << " --version show version number and exit\n";
}
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) {
std::string arg {optarg ? optarg : ""};
switch (opt_char) {
case 0: {
// handle long-only options here
switch (opt_val) {
case 1:
std::cout << {PROJECT}_version() << std::endl;
return EXIT_SUCCESS;
}
break;
}
case 'h':
print_help();
return EXIT_SUCCESS;
case '?':
throw std::runtime_error("unrecognized option");
}
}
if (optind == argc) {
// here when no file args
}
for (int i = optind; i < argc; ++i) {
try {
// process file argv[i]
} catch (const std::runtime_error& ex) {
std::cerr << "{PROJECT}: " << ex.what() << '\n';
}
}
std::cout << "hello, {PROJECT}\n";
} catch (const std::exception& ex) {
std::cerr << "{PROJECT}: " << ex.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

5
tpl/main.simple.cpp Normal file
View File

@ -0,0 +1,5 @@
#include <iostream>
int main() {
}

127
tpl/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

36
tpl/premake.lib.make Normal file
View File

@ -0,0 +1,36 @@
# Define linker flags here, like: -lsqlite3 -lpthread
LDLIBS :=
# 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 library, not a tool.
PRODUCT := lib
# 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
# Set to 0 if you want to create a library header by hand.
# By default, it will concatenate all headers, leaving out
# lines containing: @exclude
GENERATELIBHEADER := 1
# 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

27
tpl/premake.plugin.make Normal file
View File

@ -0,0 +1,27 @@
# Define linker flags here, like: -lsqlite3 -lpthread
LDLIBS :=
# 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)
# Change 'plugin' to your desired extension
PLUGIN := $(PROJ).plugin
# Single source of truth for version (currently unused).
MAJOR := 1
MINOR := 0
PATCH := 0
# Specify desired C++ standard for this project.
# Include main project's src dir for header searching.
CXXFLAGS += -std=c++20 -I../../src
# Change 'app' to product name for which this is a plugin.
# Change 'plugins' to the desired directory name for installed plugins.
INSTALLDIRNAME := app/plugins
# Specify any other files here that need to be installed alongside the plugin.
EXTRAFILES :=

31
tpl/premake.tool.make Normal file
View File

@ -0,0 +1,31 @@
# Define linker flags here, like: -lsqlite3 -lpthread
LDLIBS :=
# 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

6
tpl/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
tpl/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
tpl/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);
}

59
tpl/tool.1 Normal file
View File

@ -0,0 +1,59 @@
.Dd $Mdocdate$
.Dt {PROJECT} 1
.Os
.Sh NAME
.Nm {PROJECT}
.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.

19
tpl/version.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "version.hpp"
#include <sstream>
std::string {PROJECT}_version() {
#include "version.inc"
#include "commit.inc"
std::ostringstream oss;
oss << "{PROJECT} 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
tpl/version.hpp Normal file
View File

@ -0,0 +1,8 @@
#ifndef _{PROJ}_VERSION_H_
#define _{PROJ}_VERSION_H_
#include <string>
std::string {PROJECT}_version();
#endif // _{PROJ}_VERSION_H_