diff --git a/build-aux/Makefile.2.in b/build-aux/Makefile.2.in new file mode 100644 index 00000000..10f17a0d --- /dev/null +++ b/build-aux/Makefile.2.in @@ -0,0 +1,217 @@ +# @configure_input@ +# +# Compiles packages written in the Calc DSL. +# +# Copyright (C) 2018 R-T Specialty, LLC. +# +# 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 3 of the License, 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, see . +# +# Note that this build process is unconventional in order to avoid the startup +# costs that would be associated with executing dslc with each and every package +# (see the other Makefile for more information). Therefore, everything is +# written to .cqueue for later processing by dslc. +# +# The issue of re-building based on timestamps---which Make would normally take +# care of exclusively---must also be given special care now that we are handling +# the building separately from Make. Each enqueued request also touches the +# destination file to update its timestamp, ensuring that it is seen by Make as +# modified (as if it were compiled) and therefore will trigger the building of +# the targets that depend upon it. In the case of the object files (xmlo), a +# temporary file is created when it is enqueued. As part of the queued request +# for compilation is a request to delete this temporary file. In the event that +# the build fails, this temporary file will be seen and will force a rebuild of +# the file, despite its timestamp. +# +# The same issue does not exist for xmle, js, and html files, since they have +# linear dependency trees and dslc will rm the file on failure, which +# obliterates the timestamp. +# # + +path_rates := $(path_suppliers)/rates +path_map := map +path_c1map := $(path_map)/c1 +path_dsl := rater +path_ui := ui +path_suppliers := suppliers +path_lv := lovullo +path_srv := srv + +src_suppliers := $(wildcard $(path_suppliers)/*.xml) +src_map := $(wildcard $(path_map)/*.xml) +src_c1map := $(wildcard $(path_c1map)/*.xml) + +dest_summary_html := $(patsubst \ + $(path_suppliers)/%.xml, \ + $(path_suppliers)/%.html, \ + $(src_suppliers)) +dest_standalone := $(patsubst \ + $(path_suppliers)/%.xml, \ + $(path_suppliers)/%.js, \ + $(src_suppliers)) +dest_map := $(patsubst \ + $(path_map)/%.xml, \ + $(path_map)/%.xmle, \ + $(src_map)) +dest_c1map := $(patsubst \ + $(path_c1map)/%.xml, \ + $(path_c1map)/%.php, \ + $(src_c1map)) + +compiled_suppliers := $(src_suppliers:.xml=.xmlo) +linked_suppliers := $(src_suppliers:.xml=.xmle) + +comma := , +extless_supp_delim := $(subst .xml,,$(subst .xml ,$(comma),$(src_suppliers))) + +cqueue=.cqueue + +ant = @ANT@ -e + +.DELETE_ON_ERROR: + +.PHONY: default clean \ + interp-rate-tables summary-html c1map \ + standalones program-ui program-ui-immediate program-data-copy \ + do-build version FORCE + +# these files will never be deleted when Make considers them to be intermediate +# (e.g. when building summary pages), since they are still needed or take a +# while to build +.PRECIOUS: %.js %.xml %.xmle %.xmlo + +SHELL = /bin/bash -O extglob + +default: program-ui c1map FORCE + +program-ui: standalones ui/package.js ui/Program.js program-ui-immediate +program-ui-immediate: ui/html/index.phtml + +include suppliers.mk + +# starts with a fresh cqueue +prexmlo: + @>$(cqueue) + +summary-html: $(dest_summary_html) ; + +%.html: %.js + @echo "summary $*.xmle $@" >>.cqueue + @touch $@ + +standalones: $(dest_standalone) +%.xmle: %.xmlo + @echo "link $< $@" >>.cqueue + @touch $@ +%.js: %.xmle + @echo "standalone $< $@" >>.cqueue + @touch $@ + +# C1 XML +c1map: $(dest_c1map) +%.php: %.xml + @echo "c1map $< $@" >>.cqueue + @touch $@ + +%.dot: %.xmlo + @echo "dot $< $@" >> .cqueue +%.dote: %.xmle + @echo "dot $< $@" >> .cqueue + +%.svg: %.dote + dot -Tsvg "$<" > "$@" +%.svg: %.dot + dot -Tsvg "$<" > "$@" + +%.xml: %.dat + rater/tools/tdat2xml $< > $@ + +%.xml: %.typelist + rater/tame/build-aux/list2typedef $(*F) < $< > $@ + +%.csvo: %.csvm + rater/tools/csvm2csv $< > $@ +%.csvo: %.csvi + rater/tools/csvi $< > $@ +%.csvo: %.csv + cp $< $@ + +%.xml: %.csvo + rater/tools/csv2xml $< > $@ + +version: .version.xml +.version.xml: FORCE + git log HEAD^.. -1 --pretty=format:'%h' > .version.xml + +ui/program.expanded.xml: ui/program.xml | .version.xml + @echo "progui-expand $< $@" >> .cqueue +ui/Program.js: ui/program.expanded.xml ui/package.js + @echo "progui-class $< $@ include-path=../../../ui/" >> .cqueue +ui/html/index.phtml: ui/program.expanded.xml + @echo "progui-html $< $@ out-path=./" >> .cqueue +ui/package-dfns.xmlo: ui/package-dfns.xml +ui/package-dfns.xml: ui/program.expanded.xml + @echo "progui-pkg $< $@" >> .cqueue +ui/package-map.xmlo: ui/package-map.xml +ui/package-map.xml: ui/program.expanded.xml ui/package-dfns.xml + @echo "progui-pkg-map $< $@" >> .cqueue + +# for the time being, this does not depend on clean-rate-tables because $(ant) will +specs: + $(MAKE) -C doc/specs + # +# this will eventually go away once we don't have X-repo klugery + +# for the time being, this does not depend on clean-rate-tables because ant will +# run it +clean: + find $(path_suppliers) $(path_map) $(path_c1map) common/ rater/core rater/lv \( \ + -name '*.xmlo' \ + -o -name '*.xmle' \ + -o -name '*.js' \ + -o -name '*.html' \ + -o -name '*.dep' \ + -o -name '*.tmp' \ + \) -exec rm -v {} \; + rm -rf $(path_ui)/package-dfns.* \ + $(path_ui)/package-map.* \ + $(path_ui)/program.expanded.xml \ + $(path_ui)/include.js \ + $(path_ui)/Program.js \ + $(path_ui)/html + find . -path '*/tables/*.csvm' -o -path '*/territories/*.dat' \ + | sed 's/\.csvm$$/\.xml/; s/\.dat$$/\.xml/' \ + | xargs rm -fv + +# generates a Makefile that will properly build all package dependencies; note +# that territory and rate packages also have includes; see top of this file for +# an explanation +suppliers.mk: + $(ant) pkg-dep \ + && mv $(path_ui)/program.dep $(path_ui)/package-dfns.dep + xmlo_cmd='@echo "validate $$(patsubst %.tmp,%.xml,$$<) $$@" >> .cqueue \ + && echo "compile $$(patsubst %.tmp,%.xml,$$<) $$@" >> .cqueue \ + && echo "rm $$(patsubst %.xmlo,%.tmp,$$@)" >> .cqueue \ + && touch $$@ \ + && touch -d +1sec $$(patsubst %.xmlo,%.tmp,$$@) >> .cqueue' \ + ./rater/tools/gen-make common/ $(path_suppliers)/ $(path_dsl)/ $(path_map)/ $(path_ui)/ >$@ + +me-a-sandwich: + @test $$EUID -eq 0 \ + && echo 'You actually ran me as root? Are you insane!?' \ + || echo 'Make it yourself.' + +# simply forces a job to run, thereby forcing the invocation of the secondary +# Makefile (this is not explicitly required, because of prepare, but signifies +# intent and is self-documenting) +FORCE: ; diff --git a/build-aux/Makefile.am b/build-aux/Makefile.am new file mode 100644 index 00000000..4ddec3b9 --- /dev/null +++ b/build-aux/Makefile.am @@ -0,0 +1,182 @@ +# @configure_input@ +# +# TAME Makefile +# +# Copyright (C) 2018 R-T Specialty, LLC. +# +# 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 3 of the License, 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, see . +# +# This fragment exists as a kluge to provide support for running a command +# after all targets have been run (in this case, dslc). +# +# A list of everything to be compiled is output into .cqueue, which is then +# picked up by dslc; this avoids the overhead of starting the JVM, +# recompiling XSL stylesheets, etc, which is quite substantial. +# +# !!! Unfortunately, this does not yet support parallel job execution. + +path_rates := $(path_suppliers)/rates +path_map := map +path_c1map := $(path_map)/c1 +path_dsl := rater +path_ui := ui +path_tests := test +path_suppliers := suppliers +path_lv := lovullo +path_srv := srv +path_lvroot := lvroot +path_intralov_root := "intralov-root/@program@" + +.PHONY: FORCE prepare program-data-copy lvroot program-ui-immediate test + +JAVA_HEAP_SIZE ?= 5120M +JAVA_STACK_SIZE ?= 5M + +.DELETE_ON_ERROR: + +# less verbose output; all to runlog +define saneout + time -f 'total time: %E' awk ' \ + BEGIN { e=0; w=0; } \ + { printf "[%d] ", systime() >> ".runlog"; print >> ".runlog"; } \ + /^~~~~\[begin /,/^~~~~\[end / { next } \ + /^rm / { next } \ + /^Exception|^\t+at / { \ + if ( /^E/ ) { \ + print; \ + print "Stack trace written to .runlog"; \ + } \ + next; \ + } \ + /[Ww]arning:|[Nn]otice:/ { printf "\033[0;33m"; w++; out=1; } \ + /[Ff]atal:/ { printf "\033[0;31m"; out=1; } \ + /!|[Ee]rror:/ { printf "\033[0;31m"; e++; out=1; } \ + /internal:/ { printf "\033[0;35m"; out=1; } \ + /internal error:/ { printf "\033[1m"; out=1; } \ + /^[^[]/ || out { print; printf "\033[0;0m"; out=0; } \ + END { printf "%d error(s); %d warning(s).\n", e, w; } \ + ' +endef + +define _go + touch .cqueue \ + && ( test -s .cqueue || echo "Nothing to be done for \`$@'." ) \ + && echo "$(JAVA_HEAP_SIZE) $(JAVA_STACK_SIZE)" \ \ + && CLASSPATH="$(RATER_CLASSPATH):rater/src/dslc.jar" \ + $(JAVA) -Xmx$(JAVA_HEAP_SIZE) -Xss$(JAVA_STACK_SIZE) \ + com.lovullo.dslc.DslCompiler < .cqueue 2>&1 \ + | $(saneout); \ + exit $${PIPESTATUS[0]}; \ + @>.cqueue +endef + +SHELL = /bin/bash -O extglob + +all: program-data-copy + +program-ui-immediate: + @>.cqueue + @$(MAKE) --no-print-directory -f Makefile.2 program-ui-immediate + @$(MAKE) program-data-copy + @$(_go) + +program-data-copy: + @>.cqueue + @$(MAKE) --no-print-directory -f Makefile.2 standalones program-ui c1map + @$(_go) + mkdir -p "$(path_lv)/src/node/program/rater/programs/@program@" + mkdir -p "$(path_lv)/src/node/program/classify" + mkdir -p "$(path_lv)/src/node/program/ui/custom" + mkdir -p "$(path_lv)/src/_gen/scripts/program/@program@" + mkdir -p "$(path_lv)/src/_gen/views/scripts/quote/@program@" + mkdir -p "$(path_lv)/src/lib/c1/interfaces/c1/contract/@program@" + mkdir -p "$(path_lv)/misc/rater/programs" + mkdir -p "$(path_lv)/src/www/scripts/program" + cp -v .version.xml \ + "$(path_lv)/misc/rater/programs/.version-@program@.xml" + cp -v "$(path_ui)/custom.js" \ + "$(path_lv)/src/www/scripts/program/@program@.js" + cp -v "$(path_ui)/"!(custom|package|include).js \ + "$(path_lv)/src/node/program/ui/custom/" + cp -v "$(path_srv)/rater.js" \ + "$(path_lv)/src/node/program/rater/programs/@program@.js" + cp -v "$(path_ui)/package.js" \ + "$(path_lv)/src/node/program/classify/@program@.js" + cp -v "$(path_ui)/"{Program,include,package}.js \ + "$(path_lv)/src/_gen/scripts/program/@program@/" + cp -vr "$(path_ui)/html/"* \ + "$(path_lv)/src/_gen/views/scripts/quote/@program@/" + cp -v "$(path_suppliers)/"*.js \ + "$(path_lv)/src/node/program/rater/programs/@program@" + test ! -d "$(path_c1map)" || cp -v "$(path_c1map)/"*.php \ + "$(path_lv)/src/lib/c1/interfaces/c1/contract/@program@/" + ant -f "$(path_lv)/build.xml" js-mod-order + +# TODO: merge this and the above +lvroot: prepare + mkdir -p "$(path_lvroot)/src/node/program/rater/programs/@program@" + mkdir -p "$(path_lvroot)/src/node/program/classify" + mkdir -p "$(path_lvroot)/src/node/program/ui/custom" + mkdir -p "$(path_lvroot)/src/_gen/scripts/program/@program@" + mkdir -p "$(path_lvroot)/src/_gen/views/scripts/quote/@program@" + mkdir -p "$(path_lvroot)/src/www/scripts/program" + mkdir -p "$(path_lvroot)/src/lib/c1/interfaces/c1/contract/@program@" + cp -v "$(path_srv)/rater.js" \ + "$(path_lvroot)/src/node/program/rater/programs/@program@.js" + cp -v "$(path_suppliers)/"*.js \ + "$(path_lvroot)/src/node/program/rater/programs/@program@" + cp -v "$(path_ui)/package.js" \ + "$(path_lvroot)/src/node/program/classify/@program@.js" + cp -v "$(path_ui)/"{Program,include,package}.js \ + "$(path_lvroot)/src/_gen/scripts/program/@program@/" + cp -vr "$(path_ui)/html/"* \ + "$(path_lvroot)/src/_gen/views/scripts/quote/@program@/" + cp -v "$(path_ui)/custom.js" \ + "$(path_lvroot)/src/www/scripts/program/@program@.js" + cp -v "$(path_ui)/"*Ui.js \ + "$(path_lvroot)/src/node/program/ui/custom/" + test ! -d "$(path_c1map)" || cp -v "$(path_c1map)/"*.php \ + "$(path_lvroot)/src/lib/c1/interfaces/c1/contract/@program@/" + +intralov-root: summary-html + mkdir -p "$(path_intralov_root)/"{rater/scripts,suppliers} + ln -fL $(path_dsl)/summary.css "$(path_intralov_root)/rater" + ln -fL $(path_dsl)/scripts/*.js "$(path_intralov_root)/rater/scripts/" + ln -fL $(path_suppliers)/*.{html,js} "$(path_intralov_root)/suppliers" + + +# because of the crazy wildcard target below, we want to ignore +# some Automake-generated stuff +%.am: +%.m4: +%.ac: + +%: prepare + @if [[ "$@" != [Mm]akefile ]]; then \ + $(MAKE) --no-print-directory -f Makefile.2 $@; \ + $(_go); \ + fi + +clean: + $(MAKE) --no-print-directory -f Makefile.2 clean + +prepare: FORCE + @>.cqueue + +test: check +check-am: standalones ui/package.js + @$(path_dsl)/build-aux/progtest-runner $(path_suppliers) $(path_tests) + @$(path_dsl)/build-aux/progtest-runner ui/package.xml $(path_tests)/ui + +FORCE: ; diff --git a/build-aux/m4/calcdsl.m4 b/build-aux/m4/calcdsl.m4 new file mode 100644 index 00000000..18f8ab5d --- /dev/null +++ b/build-aux/m4/calcdsl.m4 @@ -0,0 +1,105 @@ +# Common build configuration for TAME-based build systems +# +# Copyright (C) 2017 R-T Specialty, LLC. +# +# 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 3 of the License, 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, see . +# +# To use, include this in your configure.ac: +# m4_define(`calc_root', path/to/calc/root) +# m4_include([path/to/calc/root/build-aux/m4/calcdsl.m4]) +## + +# We use an M4 value (calc_root, specifically); this allows us to cleanly +# reference it +m4_pattern_allow([defn]) + +# Initialize Automake, indicating that we use non-standard conventions +AC_CONFIG_AUX_DIR(m4_defn(`calc_root')/build-aux) +AM_INIT_AUTOMAKE([foreign]) + +# Configuration values that can be provided via environment variables or the +# command line at configure- or build-time. +AC_ARG_VAR([JAVA], [The Java executable]) +AC_ARG_VAR([ANT], [Apache Ant]) +AC_ARG_VAR([DSLC_JAR], [Path to DSL Compiler JAR]) +AC_ARG_VAR([TAME], [Path to TAME]) +AC_ARG_VAR([RATER_CLASSPATH], [DSL Compiler Saxon class path]) +AC_ARG_VAR([PROGUI_TEST_PATH], [Path to JavaScript tests for Program UI]) + +# Required version of TAME +AC_SUBST([tame_needed_ver], [1.0.0]) + +# Auto-discover Java and Ant paths +AC_CHECK_PROGS(JAVA, [java]) +AC_CHECK_PROGS(ANT, [ant]) + +AS_IF([test "$JAVA"],, + [AC_MSG_ERROR([missing java])]) +AS_IF([test "$ANT"],, + [AC_MSG_ERROR([missing ant])]) + +# Automake runs before shell is available, thus the separate m4 variable +CALCROOT="m4_defn(`calc_root')" + +# Checks to ensure that dslc is built, and gives instructions on how to +# build it otherwise. We do not want to build that for them---that can be +# added to a bootstrap script, but isn't permissible in build scripts. +AS_IF([test ! "$DSLC_JAR"], + [AC_CHECK_FILE([$CALCROOT/src/dslc.jar], + [AC_SUBST([DSLC_JAR], [$CALCROOT/src/dslc.jar])], + [AC_MSG_ERROR( + [Please run `make` in $CALCROOT to build the DSL compiler.])])], + []) + +# TAME is the compiler (whereas dslc invokes it, keeps things in memory, etc) +AS_IF([test ! "$TAME"], + [AC_CHECK_FILE([$CALCROOT/tame], + [AC_SUBST([TAME], [$CALCROOT/tame])], + [AC_MSG_ERROR( + [TAME not found])])], + []) + +AC_MSG_CHECKING([TAME version]) + +AC_SUBST_FILE([tame_version]) +tame_version=$( cat "$TAME/VERSION" ) + +# We get subtle errors or potential compiler bugs if the TAME version is +# incorrect; check for >= the required version +AS_VERSION_COMPARE([$tame_version], [$tame_needed_ver], + [ + AC_MSG_RESULT([$tame_version]) + AC_MSG_ERROR([TAME version $tame_needed_ver or greater required]) + ], + [AC_MSG_RESULT([$tame_version])], + [AC_MSG_RESULT([$tame_version (>$tame_needed_ver)])]) + +# @program@ in *.in files will be replaced with the program name provided by AC_INIT +AC_SUBST([program], AC_PACKAGE_NAME) + +# Final files to be output by `configure'. The path before the colon is the +# destination name; after the colon is the source. +AC_CONFIG_FILES(Makefile:m4_defn(`calc_root')/build-aux/Makefile.in + Makefile.2:m4_defn(`calc_root')/build-aux/Makefile.2.in) + +# Generate configure script +AC_OUTPUT + +# we want this to run as part of the configure script, not during M4 +# expansion +"$CALCROOT/build-aux/suppmk-gen" + +AC_MSG_NOTICE([complete + +You may now run `make` to build.]) diff --git a/build-aux/progtest-runner b/build-aux/progtest-runner new file mode 100755 index 00000000..306cddc5 --- /dev/null +++ b/build-aux/progtest-runner @@ -0,0 +1,54 @@ +#!/bin/bash +# Run test cases for supplier +# +# Copyright (C) 2018 R-T Specialty, LLC. +# +# 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 3 of the License, 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, see . +## + +declare path_suppliers="${1?Missing supplier path}" +declare path_tests="${2?Missing supplier test path}" + +declare -i result=0 + +declare suppliers + +# if a file was provided, use it as the sole supplier; otherwise, +# treat it as a directory of suppliers +if [ -f "$path_suppliers" ]; then + suppliers=( "$path_suppliers" ) + path_suppliers=$( dirname "$path_suppliers" ) +else + suppliers=( "$path_suppliers"/*.xml ) +fi + +# run tests for each supplier individually +for supplier in "${suppliers[@]}"; do + base=$( basename "$supplier" .xml ) + tests=$( find -L "$path_tests"/"$base"/ -name '*.yml' ) + + echo + echo "$path_suppliers/$base" + sed 's/./=/g' <<< "$path_suppliers/$base" + + test -n "$tests" || { + echo "error: missing test cases for $base!" >&2 + exit 1 + } + + rater/tame/progtest/bin/runner "$path_suppliers/$base.js" $tests \ + || result=1 +done + +exit $result diff --git a/build-aux/suppmk-gen b/build-aux/suppmk-gen new file mode 100755 index 00000000..36d57176 --- /dev/null +++ b/build-aux/suppmk-gen @@ -0,0 +1,40 @@ +#!/bin/bash +# Configuration script to be run before `make' +# +# Copyright (C) 2018 R-T Specialty, LLC. +# +# 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 3 of the License, 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, see . +## + +echo "Generating suppliers.mk..." +# TODO: kluge; do properly. +rm -f suppliers.mk +make suppliers.mk + +# XXX: paths are hard-coded here! +while read csv; do + csvbase="${csv%%.*}" + echo "$csvbase.xmlo: $csvbase.xml" + echo "$csvbase.xml: $csvbase.csvo" +done < <( find suppliers common -regex '^.+\.csv.?$' ) \ + >> suppliers.mk + +while read tdat; do + tbase="${tdat%%.*}" + echo "$tbase.xmlo: $tbase.xml rater/core/tdat.xmlo" + echo "$tbase.xml: $tdat" + echo -e "\trater/tools/tdat2xml \$< > \$@" +done < <( find suppliers common -regex '^.+territories/.+\.dat$' ) \ + >> suppliers.mk +