~emersion/mrsh-dev

Add configure script and Makefile v3 PROPOSED

Drew DeVault: 1
 Add configure script and Makefile

 8 files changed, 399 insertions(+), 15 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/mrsh-dev/patches/6794/mbox | git am -3
Learn more about email & git

[PATCH v3] Add configure script and Makefile Export this patch

The configure script is POSIX sh and the Makefile is POSIX make.
---
This removes the % rules, but as a consequence moves the list of source
files into the configure script and generates rules for them. This also
no longer supports out of tree builds, in order to take advantage of
POSIX inference rules.

 .gitignore             |   4 +
 Makefile               |  88 +++++++++++++
 configure              | 280 +++++++++++++++++++++++++++++++++++++++++
 mkpc                   |  12 ++
 test/conformance/if.sh |  16 +--
 test/harness.sh        |   6 +-
 test/meson.build       |   2 +-
 test/pipeline.sh       |   6 +-
 8 files changed, 399 insertions(+), 15 deletions(-)
 create mode 100644 Makefile
 create mode 100755 configure
 create mode 100755 mkpc
 mode change 100644 => 100755 test/harness.sh

diff --git a/.gitignore b/.gitignore
index 4c07ad0..32fe6d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,7 @@ dkms.conf

/build
/build-*

.build
highlight
mrsh
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5243c48
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,88 @@
.POSIX:
.SUFFIXES:
OUTDIR=.build
include $(OUTDIR)/config.mk

INCLUDE=-Iinclude

public_includes=\
		include/mrsh/arithm.h \
		include/mrsh/array.h \
		include/mrsh/ast.h \
		include/mrsh/buffer.h \
		include/mrsh/builtin.h \
		include/mrsh/entry.h \
		include/mrsh/getopt.h \
		include/mrsh/hashtable.h \
		include/mrsh/parser.h \
		include/mrsh/shell.h

tests=\
		test/conformance/if.sh \
		test/case.sh \
		test/command.sh \
		test/for.sh \
		test/function.sh \
		test/loop.sh \
		test/pipeline.sh \
		test/subshell.sh \
		test/syntax.sh \
		test/ulimit.sh \
		test/word.sh

include $(OUTDIR)/cppcache

.SUFFIXES: .c .o

.c.o:
	@mkdir -p $$(dirname "$@")
	@printf 'CC\t$@\n'
	@touch $(OUTDIR)/cppcache
	@grep $< $(OUTDIR)/cppcache >/dev/null || \
		$(CPP) $(INCLUDE) -MM -MT $@ $< >> $(OUTDIR)/cppcache
	@$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $<

$(OUTDIR)/libmrsh.a: $(libmrsh_objects)
	@printf 'AR\t$@\n'
	@$(AR) -csr $@ $(libmrsh_objects)

libmrsh.so.$(SOVERSION): $(OUTDIR)/libmrsh.a
	@printf 'LD\t$@\n'
	@$(CC) -shared $(LDFLAGS) -o $@ $<

$(OUTDIR)/mrsh.pc:
	@printf 'MKPC\t$@\n'
	@PREFIX=$(PREFIX) ./mkpc $@

mrsh: $(OUTDIR)/libmrsh.a $(mrsh_objects)
	@printf 'CCLD\t$@\n'
	@$(CC) -o $@ $(LIBS) $(mrsh_objects) -L$(OUTDIR) -lmrsh

highlight: $(OUTDIR)/libmrsh.a $(highlight_objects)
	@printf 'CCLD\t$@\n'
	@$(CC) -o $@ $(LIBS) $(highlight_objects) -L$(OUTDIR) -lmrsh

check: mrsh $(tests)
	@for t in $(tests); do \
		printf '%-30s... ' "$$t" && \
		MRSH=./mrsh REF_SH=$${REF_SH:-sh} ./test/harness.sh $$t >/dev/null && \
		echo OK || echo FAIL; \
	done

install: mrsh libmrsh.so.$(SOVERSION) $(OUTDIR)/mrsh.pc
	mkdir -p $(BINDIR) $(LIBDIR) $(INCDIR)/mrsh $(PCDIR)
	install -m755 mrsh $(BINDIR)/mrsh
	install -m755 libmrsh.so.$(SOVERSION) $(LIBDIR)/libmrsh.so.$(SOVERSION)
	for inc in $(public_includes); do \
		install -m644 $$inc $(INCDIR)/mrsh/$$(basename $$inc); \
	done
	install -m644 $(OUTDIR)/mrsh.pc $(PCDIR)/mrsh.pc

clean:
	rm -rf \
		$(libmrsh_objects) \
		$(mrsh_objects) \
		$(highlight_objects) \
		mrsh higlight libmrsh.so.$(SOVERSION) $(OUTDIR)/mrsh.pc

.PHONY: all install clean check
diff --git a/configure b/configure
new file mode 100755
index 0000000..5b45848
--- /dev/null
+++ b/configure
@@ -0,0 +1,280 @@
#!/bin/sh -e
SOVERSION=0.0.0

pkg_config=${PKG_CONFIG:-pkg-config}
outdir=${OUTDIR:-.build}
srcdir=${SRCDIR:-$(dirname "$0")}
CC=${CC:-cc}
LIBS=

use_readline=-1
readline=readline

for arg
do
	case "$arg" in
		--prefix=*)
			PREFIX=${arg#*=}
			;;
		--without-readline)
			use_readline=0
			;;
		--with-readline=*)
			use_readline=1
			readline=${arg#*=}
			;;
	esac
done

libmrsh() {
	genrules libmrsh \
		'arithm.c' \
		'array.c' \
		'ast_print.c' \
		'ast.c' \
		'buffer.c' \
		'builtin/alias.c' \
		'builtin/bg.c' \
		'builtin/break.c' \
		'builtin/builtin.c' \
		'builtin/cd.c' \
		'builtin/colon.c' \
		'builtin/command.c' \
		'builtin/dot.c' \
		'builtin/eval.c' \
		'builtin/exit.c' \
		'builtin/export.c' \
		'builtin/false.c' \
		'builtin/fg.c' \
		'builtin/getopts.c' \
		'builtin/pwd.c' \
		'builtin/read.c' \
		'builtin/set.c' \
		'builtin/shift.c' \
		'builtin/times.c' \
		'builtin/true.c' \
		'builtin/type.c' \
		'builtin/ulimit.c' \
		'builtin/umask.c' \
		'builtin/unalias.c' \
		'builtin/unset.c' \
		'builtin/unspecified.c' \
		'builtin/wait.c' \
		'getopt.c' \
		'hashtable.c' \
		'parser/arithm.c' \
		'parser/parser.c' \
		'parser/program.c' \
		'parser/word.c' \
		'shell/arithm.c' \
		'shell/entry.c' \
		'shell/job.c' \
		'shell/path.c' \
		'shell/process.c' \
		'shell/redir.c' \
		'shell/shell.c' \
		'shell/task/pipeline.c' \
		'shell/task/simple_command.c' \
		'shell/task/task.c' \
		'shell/task/word.c' \
		'shell/word.c'
}

mrsh() {
	if [ $use_readline -eq 1 ]
	then
		genrules mrsh \
			'main.c' \
			'frontend/readline.c'
	else
		genrules mrsh \
			'main.c' \
			'frontend/basic.c'
	fi
}

highlight() {
	genrules highlight highlight.c
}

genrules() {
	target="$1"
	shift
	printf '# Begin generated rules for %s\n' "$target"
	for file in "$@"
	do
		file="${file%.*}"
		printf '%s.o: %s.c\n' "$file" "$file"
	done
	printf '%s_objects=\\\n' "$target"
	n=0
	for file in "$@"
	do
		file="${file%.*}"
		n=$((n+1))
		if [ $n -eq $# ]
		then
			printf '\t%s.o\n' "$file"
		else
			printf '\t%s.o \\\n' "$file"
		fi
	done
	printf '# End generated rules for %s\n' "$target"
}

append_cflags() {
	for flag
	do
		CFLAGS="$(printf '%s \\\n\t%s' "$CFLAGS" "$flag")"
	done
}

append_ldflags() {
	for flag
	do
		LDFLAGS="$(printf '%s \\\n\t%s' "$LDFLAGS" "$flag")"
	done
}

append_libs() {
	for flag
	do
		LIBS="$(printf '%s \\\n\t%s' "$LIBS" "$flag")"
	done
}

test_cflags() {
	[ ! -e "$outdir"/check.c ] && cat <<-EOF > "$outdir"/check.c
	int main(void) { return 0; }
	EOF
	if $CC "$@" -o /dev/null "$outdir"/check.c >/dev/null 2>&1
	then
		append_cflags "$@"
	else
		return 1
	fi
}

test_ldflags() {
	[ ! -e "$outdir"/check.c ] && cat <<-EOF > "$outdir"/check.c
	int main(void) { return 0; }
	EOF
	if $CC "$@" -o /dev/null "$outdir"/check.c >/dev/null 2>&1
	then
		append_ldflags "$@"
	else
		return 1
	fi
}

mkdir -p $outdir

for flag in \
	-g -std=c99 -pedantic -Werror -Wundef -Wlogical-op \
	-Wmissing-include-dirs -Wold-style-definition -Wpointer-arith -Winit-self \
	-Wfloat-equal -Wstrict-prototypes -Wredundant-decls \
	-Wimplicit-fallthrough=2 -Wendif-labels -Wstrict-aliasing=2 -Woverflow \
	-Wformat=2 -Wno-missing-braces -Wno-missing-field-initializers \
	-Wno-unused-parameter
do
	printf "Checking for $flag... "
	if test_cflags "$flag"
	then
		echo yes
	else
		echo no
	fi
done

for flag in -fPIC -Wl,--no-undefined -Wl,--as-needed
do
	test_ldflags "$flag"
done

soname=libmrsh.so.$(echo "$SOVERSION" | cut -d. -f1)
printf "Checking for specifying soname for shared lib... "
if ! \
	test_ldflags -Wl,-soname,$soname || \
	test_ldflags -Wl,-install_name,$soname
then
	echo no
	echo "Unable to specify soname (is $(uname) supported?)" >&2
	exit 1
else
	echo yes
fi

printf "Checking for exported symbol restrictions... "
if ! \
	test_ldflags -Wl,--version-script="libmrsh.gnu.sym" || \
	test_ldflags -Wl,-exported_symbols_list,"libmrsh.darwin.sym"
then
	echo no
	echo "Unable to specify exported symbols (is $(uname) supported?)" >&2
	exit 1
else
	echo yes
fi

if [ $use_readline -eq -1 ]
then
	printf "Checking for readline... "
	if $pkg_config readline
	then
		readline=readline
		use_readline=1
		append_cflags -DHAVE_READLINE
		echo yes
	else
		echo no
	fi
fi
if [ $use_readline -eq -1 ]
then
	printf "Checking for libedit... "
	if $pkg_config libedit
	then
		echo yes
		readline=libedit
		use_readline=1
		append_cflags -DHAVE_EDITLINE
	else
		echo no
	fi
fi

if [ $use_readline -eq 1 ]
then
	append_cflags $($pkg_config --cflags $readline)
	append_libs $($pkg_config --libs $readline)
	if [ "$readline" = "readline" ]
	then
		# Undo GNU_SOURCE
		append_cflags -U_GNU_SOURCE
	fi
fi

printf "Creating $outdir/config.mk... "
cat <<EOF > "$outdir"/config.mk
SOVERSION=$SOVERSION
CC=$CC
PREFIX=${PREFIX:-/usr/local}
_INSTDIR=\$(DESTDIR)\$(PREFIX)
BINDIR?=${BINDIR:-\$(_INSTDIR)/bin}
LIBDIR?=${LIBDIR:-\$(_INSTDIR)/lib}
INCDIR?=${INCDIR:-\$(_INSTDIR)/include}
MANDIR?=${MANDIR:-\$(_INSTDIR)/share/man}
PCDIR?=${PCDIR:-\$(_INSTDIR)/lib/pkgconfig}
CFLAGS=${CFLAGS}
LDFLAGS=${LDFLAGS}
LIBS=${LIBS}
SRCDIR=${srcdir}

all: mrsh highlight libmrsh.so.\$(SOVERSION) \$(OUTDIR)/mrsh.pc
EOF
libmrsh >>"$outdir"/config.mk
mrsh >>"$outdir"/config.mk
highlight >>"$outdir"/config.mk
echo done

touch $outdir/cppcache
diff --git a/mkpc b/mkpc
new file mode 100755
index 0000000..9916e9b
--- /dev/null
+++ b/mkpc
@@ -0,0 +1,12 @@
#!/bin/sh
cat <<EOF > $1
prefix=$PREFIX
libdir=\${prefix}/lib
includedir=\${prefix}/include

Name: mrsh
Description: POSIX shell library
Version: 0.0.0
Libs: -L\${libdir} -lmrsh
Cflags: -I\${includedir}
EOF
diff --git a/test/conformance/if.sh b/test/conformance/if.sh
index 258fbd7..0be4160 100644
--- a/test/conformance/if.sh
+++ b/test/conformance/if.sh
@@ -7,20 +7,20 @@
# pass
# pass

echo >&2 "-> if..fi with true condition"
echo "-> if..fi with true condition"
if true
then
	echo pass
fi

echo >&2 "-> if..fi with false condition"
echo "-> if..fi with false condition"
if false
then
	echo >&2 "fail: This branch should not have run" && exit 1
fi
echo pass

echo >&2 "-> if..else..fi with true condition"
echo "-> if..else..fi with true condition"
if true
then
	echo pass
@@ -28,7 +28,7 @@ else
	echo >&2 "fail: This branch should not have run" && exit 1
fi

echo >&2 "-> if..else..fi with false condition"
echo "-> if..else..fi with false condition"
if false
then
	echo >&2 "fail: This branch should not have run" && exit 1
@@ -36,7 +36,7 @@ else
	echo pass
fi

echo >&2 "-> if..else..fi with true condition"
echo "-> if..else..fi with true condition"
if true
then
	echo pass
@@ -44,7 +44,7 @@ else
	echo >&2 "fail: This branch should not have run" && exit 1
fi

echo >&2 "-> if..else..elif..fi with false condition"
echo "-> if..else..elif..fi with false condition"
if false
then
	echo >&2 "fail: This branch should not have run" && exit 1
@@ -55,7 +55,7 @@ else
	echo >&2 "fail: This branch should not have run" && exit 1
fi

echo >&2 "-> test exit status"
echo "-> test exit status"
if true
then
	( exit 10 )
@@ -71,7 +71,7 @@ else
fi
[ $# -eq 20 ] || { echo >&2 "fail: Expected status code = 20" && exit 1; }

echo >&2 "-> test alternate syntax"
echo "-> test alternate syntax"
# These tests are only expected to parse, they do not make assertions
if true; then true; fi
if true; then true; else true; fi
diff --git a/test/harness.sh b/test/harness.sh
old mode 100644
new mode 100755
index d05a744..885aa74
--- a/test/harness.sh
+++ b/test/harness.sh
@@ -1,11 +1,11 @@
#!/bin/sh
dir=$(dirname "$0")
testcase="$dir/$1"
testcase="$1"

echo >&2 "Running with mrsh"
echo "Running with mrsh"
mrsh_out=$("$MRSH" "$testcase")
mrsh_ret=$?
echo >&2 "Running with reference shell"
echo "Running with reference shell"
ref_out=$("$REF_SH" "$testcase")
ref_ret=$?
if [ $mrsh_ret -ne $ref_ret ] || [ "$mrsh_out" != "$ref_out" ]
diff --git a/test/meson.build b/test/meson.build
index 1df5daa..7e18c0d 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -19,7 +19,7 @@ test_files = [

foreach test_file : test_files
	test(
		test_file,
		join_paths(meson.current_source_dir(), test_file),
		harness,
		env: [
			'MRSH=@0@'.format(mrsh_exe.full_path()),
diff --git a/test/pipeline.sh b/test/pipeline.sh
index 3d60740..7c2d449 100644
--- a/test/pipeline.sh
+++ b/test/pipeline.sh
@@ -1,12 +1,12 @@
#!/bin/sh

echo >&2 "Pipeline with 1 command"
echo "Pipeline with 1 command"
echo "a b c d"

echo >&2 "Pipeline with 2 commands"
echo "Pipeline with 2 commands"
echo "a b c d" | sed s/b/B/

echo >&2 "Pipeline with 3 commands"
echo "Pipeline with 3 commands"
echo "a b c d" | sed s/b/B/ | sed s/c/C/

echo >&2 "Pipeline with bang"
-- 
2.22.0



      
      View this thread in the archives