Project

General

Profile

1
##### Configuration
2

    
3
log ?= $(if $(test),,1)
4
profile ?=
5
exts ?= csv xml
6
test_n ?= 2
7
tablesSort ?= plots organisms stems
8

    
9
##### Vars/functions
10

    
11
selfDir_uZPPqC := $(dir $(lastword $(MAKEFILE_LIST)))
12

    
13
# Make
14
SHELL := /bin/bash
15
subMake = $(MAKE) $(@F) --directory=$(@D)
16
+_ = $(+:_%=)
17
addBeforeExt = $(basename $(2))$(1)$(suffix $(2))
18

    
19
# System
20
date = $(shell date +"%Y-%m-%d-%H-%M-%S")
21

    
22
# Terminal
23
termCols := $(shell tput cols)
24
esc := '['
25
reset := $(esc)'0m'
26
emph := $(esc)'7m '
27
endEmph := ' '$(reset)
28

    
29
# Commands
30
MKDIR = mkdir -p
31
mkdir = $(MKDIR) $(@D)
32
CP = cp -p
33
diff = diff --unified=2
34
diffVerbose = $(if $(verbose),diff --side-by-side --left-column\
35
--width=$(termCols),$(diff))
36

    
37
# Paths
38
datasrc := $(notdir $(realpath .))
39
root := $(selfDir_uZPPqC)..
40
bin := $(root)/bin
41
mappings := $(root)/mappings
42

    
43
# Commands
44
join = $(bin)/join_union_sort
45
psqlOpts := --set ON_ERROR_STOP=1 --quiet
46
psqlAsBien := $(bin)/psql_vegbien $(psqlOpts)
47

    
48
##### General targets
49

    
50
all: _always maps ;
51

    
52
.SUFFIXES: # turn off built-in suffix rules
53
.SECONDARY: # don't automatically delete intermediate files
54

    
55
_always:
56
.PHONY: _always
57

    
58
clean: _always
59
	$(RM) $(all)
60

    
61
%.out: %.make _always
62
	./$* >$@
63

    
64
##### SVN
65

    
66
svn_props: _always
67
	svn propset svn:ignore $$'*.log\n*.trace\nsrc*' .
68
	$(if $(wildcard maps/),svn propset svn:ignore $$'.~*' maps)
69
	$(if $(wildcard verify/),svn propset svn:ignore $$'*.out' verify)
70
	$(if $(wildcard test/),svn propset svn:ignore $$'*.out\n*.xml' test)
71

    
72
##### Installation
73

    
74
reinstall: _always uninstall install ;
75

    
76
##### Maps
77

    
78
srcMap := maps/src.%.csv
79
fullViaMap := maps/%.full.csv
80
directMap := maps/VegBIEN.%.csv
81
allViaMaps := $(filter-out $(srcMap) $(fullViaMap) $(directMap),\
82
$(wildcard maps/*.csv))
83
via := $(firstword $(sort $(basename $(basename $(notdir $(allViaMaps))))))
84

    
85
coreMap := $(mappings)/$(via)-VegBIEN.%.csv
86
selfMap := $(mappings)/$(via).self.%.csv
87
noEmptyMap := $(mappings)/$(via)-VegBIEN.%.no_empty.csv
88

    
89
viaMaps := $(wildcard $(tablesSort:%=maps/$(via).%.csv))
90
viaMaps += $(filter-out $(viaMaps) $(fullViaMap),$(wildcard maps/$(via).*.csv))
91

    
92
autogenMaps := $(subst $(via).,VegBIEN.,$(viaMaps))
93
directMaps := $(autogenMaps) $(filter-out $(autogenMaps),\
94
$(wildcard maps/VegBIEN.*.csv))
95
tables := $(directMaps:maps/VegBIEN.%.csv=%)
96

    
97
srcMaps := $(filter-out maps/src.join.%.csv,$(wildcard maps/src.*.csv))
98
srcJoinMaps := $(srcMaps:maps/src.%.csv=maps/src.join.%.csv)
99

    
100
# Must come before $(root)/% to override it
101
$(selfMap): _always
102
	-+$(subMake)
103
# ignore errors if $(selfMap) does not exist
104

    
105
$(root)/%: _always
106
	+$(subMake)
107

    
108
maps :=
109

    
110
maps/VegBIEN.%.csv: maps/$(via).%.csv $(coreMap)
111
	$(join) <$+ >$@
112
maps += $(autogenMaps)
113

    
114
makeFullCsv = $(if $(shell test -e $(word 2,$+) && echo t),\
115
env ignore=1 $(bin)/union <$+|$(bin)/sort_map >$@,$(CP) $< $@)
116
# can't use $(wildcard) because it won't recheck file after $(selfMap) is run
117

    
118
maps/$(via).%.full.csv: maps/$(via).%.csv $(selfMap)
119
	$(makeFullCsv)
120
maps += $(patsubst maps/%.csv,maps/%.full.csv,$(viaMaps))
121

    
122
maps/src.join.%.csv: maps/src.%.csv maps/$(via).%.full.csv $(noEmptyMap)
123
	$(bin)/cols 0 0 <$<|$(bin)/join $(word 2,$+)|$(join) $(word 3,$+) >$@
124
maps += $(srcJoinMaps)
125

    
126
maps: $(maps) _always ;
127

    
128
all += $(maps)
129

    
130
##### Mapping
131

    
132
dbFile := $(firstword $(wildcard src/db.*.sql))
133
inputFiles := $(wildcard src/*.csv src/*.xml)
134

    
135
+maps = $(filter maps/% $(mappings)/%,$(+_))
136
<in = $(firstword $(filter-out $(+maps),$(+_)) $(wildcard $(exts:%=src/*.$*.%)))
137
map = $(if $(<in),<$(<in) ,$(if $(mapEnv),env $(mapEnv) ,$(error\
138
No input file src/*.$*.{$(exts)} )))$(root)/map $(+maps)
139
map2db = env out_database=vegbien $(map)
140

    
141
##### Import to VegBIEN
142

    
143
ifneq ($(dbFile)$(inputFiles),)
144

    
145
log_ = $(@:-all=)$(if $(n),.n=$(n),).$(date).log
146
trace = $(log_:.log=.trace)
147
import = -(set -x; "time" env commit=1 verbose=1\
148
$(if $(profile),profile_to=$(trace)) $(map2db)) $(if $(log),\
149
$(if $(n),,>>$(log_))) 2>&1$(if $(log),$(if $(n),|tee -a $(log_)))
150
# don't abort on import errors, which often relate to invalid input data
151

    
152
import: $(addprefix import-,$(tables)) _always ;
153

    
154
import-%: maps/VegBIEN.%.csv _always
155
	$(import)
156
# default:
157
import-%: _always ;
158

    
159
else
160
import: _always ;
161
endif
162

    
163
##### Log files from import
164

    
165
logs := $(wildcard *.log *.trace)
166

    
167
rm_logs: _always
168
	$(RM) $(logs)
169

    
170
##### Verification of import
171

    
172
verify: $(addprefix verify-,$(tables)) _always ;
173

    
174
verify-%: verify/%.ref verify/%.out _always
175
	-$(diffVerbose) $(+_)
176
# don't abort on verification errors, which are expected during development
177
# default:
178
verify-%: verify/%.out _always
179
	$(if $(shell test -e $< && echo t),cat $<)
180
# don't run if verify/%.out's default do-nothing action was used
181
# can't use $(wildcard) because it won't recheck file after verify/%.out is run
182

    
183
define verify
184
$(mkdir)
185
"time" $(psqlAsBien) --set=datasource="'$(datasrc)'" --no-align\
186
--field-separator=$$'\t' --pset=footer=off --pset=null=NULL <$< >$@
187
endef
188

    
189
verify/%.out: $(mappings)/verify.%.sql _always
190
	$(verify)
191
# default:
192
verify/%.out: _always ;
193

    
194
all += $(wildcard verify/*.out)
195

    
196
ifneq ($(dbFile),)
197
%.ref: %.ref.sql
198
	$(dbAsBien) $(db) <$< >$@
199
endif
200

    
201
##### Testing
202

    
203
ifneq ($(wildcard test/),)
204

    
205
hasOwnRef = $(filter-out %.2-step.xml,$@)
206
testRef = $(1:.2-step.xml=.xml).ref
207

    
208
define runTest
209
@echo "Testing $(abspath $@)..."
210
>$@ env test=1 n=$(test_n) $(1)
211
@(set -x; $(diff) $(call testRef,$@) $@) 2>&1 || { e=$$?;\
212
$(if $(wildcard $(call testRef,$@)),,cat $@;)\
213
$(if $(hasOwnRef),\
214
echo $(emph)"To accept new test output:"$(endEmph);\
215
echo "$(MAKE) $@-ok --directory=$(realpath .) --makefile=../input.Makefile";\
216
,\
217
echo $(emph)"Note: The preceding failed test is compared to another test's\
218
output"$(endEmph);\
219
echo $(emph)"When it fails, this always indicates a bug"$(endEmph);\
220
)\
221
exit $$e;}
222
endef
223

    
224
test2File = $(call runTest,$(map))
225

    
226
tests :=
227

    
228
test/$(via).%.xml: maps/$(via).%.full.csv _always
229
	$(test2File)
230
tests += test/$(via).%.xml
231

    
232
test/VegBIEN.%.xml: maps/VegBIEN.%.csv _always
233
	$(test2File)
234
tests += test/VegBIEN.%.xml
235

    
236
test/VegBIEN.%.2-step.xml: test/$(via).%.xml $(coreMap) _always
237
	-$(test2File)
238
# Don't abort tester if only 2-step test fails, as it's often finicky
239
tests += test/VegBIEN.%.2-step.xml
240

    
241
test/import.%.out: maps/VegBIEN.%.csv _always
242
	$(call runTest,$(map2db))
243
tests += test/import.%.out
244

    
245
testOutputs := $(foreach test,$(tests),$(tables:%=$(test)))
246

    
247
test: _always $(testOutputs) ;
248

    
249
all += $(testOutputs)
250

    
251
# Accepts a test output: make <test_output_path>-ok
252
%-ok: _always
253
	$(CP) $* $(call testRef,$*)
254

    
255
else
256
test: _always ;
257
endif
258

    
259
##### Input-type-specific
260

    
261
# Each input type needs var $(mapEnv) and targets install, uninstall
262

    
263
ifneq ($(dbFile),)
264

    
265
dbEngineExt := $(subst .,,$(suffix $(basename $(notdir $(dbFile)))))
266
db := $(datasrc)
267

    
268
#### Installation
269

    
270
install: _always db ;
271

    
272
uninstall: _always rm_db ;
273

    
274
#### DB-engine-specific
275

    
276
# Each DB engine needs vars $(dbEngine), $(dbAsBien) and targets db, rm_db
277

    
278
ifeq ($(dbEngineExt),my)
279

    
280
dbEngine := MySQL
281

    
282
bienPassword := $(shell cat $(root)/config/bien_password)
283
mysqlAs = mysql --user=$(1) --password='$(bienPassword)'
284
mysqlAsRoot := $(call mysqlAs,root)
285
dbAsBien := $(call mysqlAs,bien)
286

    
287
dbExists = $(shell echo "SHOW DATABASES LIKE '$(db)';"|$(mysqlAsRoot))
288

    
289
define createDb
290
echo "CREATE DATABASE $(db) DEFAULT CHARACTER SET latin1;"|$(mysqlAsRoot)
291
-$(mysqlAsRoot) --database=$(db) <$<
292
endef
293
# ignore errors in db import so that GRANT will still be run
294

    
295
db: $(dbFile) _always
296
	$(if $(dbExists),,$(createDb))
297
	echo "GRANT SELECT ON $(db).* TO 'bien'@'localhost';"|$(mysqlAsRoot)
298

    
299
rm_db: _always
300
	-echo "REVOKE ALL ON $(db).* FROM 'bien'@'localhost';"|$(mysqlAsRoot)
301
	echo "DROP DATABASE IF EXISTS $(db);"|$(mysqlAsRoot)
302
# ignore errors if grant not defined
303

    
304
### Unrecognized DB engine
305

    
306
else
307
$(error The DB filename $(dbFile) must be db.my.sql)
308
endif
309

    
310
# Must come after dbEngine is set
311
mapEnv := in_engine=$(dbEngine) in_database=$(db)
312

    
313
#### Unrecognized input type
314

    
315
else
316
install: _always ;
317
uninstall: _always ;
318
endif
(2-2/2)