Project

General

Profile

1
##### Configuration
2

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

    
10
##### Vars/functions
11

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

    
14
# Make
15
SHELL := /bin/bash
16
selfMake = $(MAKE) --makefile=../input.Makefile
17
subMake = $(MAKE) $(@F) --directory=$(@D)
18
+_ = $(+:_%=)
19
addBeforeExt = $(basename $(2))$(1)$(suffix $(2))
20

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

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

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

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

    
45
# Commands
46
selfMap = $(bin)/cols 0 0
47
psqlOpts := --set ON_ERROR_STOP=1 --quiet
48
psqlAsBien := $(bin)/psql_vegbien $(psqlOpts)
49

    
50
# SVN
51
addDir = $(if $(wildcard $(1)/),svn add --depth=empty $(1),svn mkdir $(1))
52
setSvnIgnore := svn propset svn:ignore
53
define addDirWithIgnore
54
$(addDir)
55
$(setSvnIgnore) $(2) $(1)
56
endef
57

    
58
##### General targets
59

    
60
all: _always maps ;
61

    
62
.SUFFIXES: # turn off built-in suffix rules
63
.SECONDARY: # don't automatically delete intermediate files
64
.DELETE_ON_ERROR: # delete target if recipe fails
65

    
66
_always:
67
.PHONY: _always
68

    
69
clean: _always
70
	$(RM) $(all)
71

    
72
%: %.make _always
73
	./$* >$@
74

    
75
%/: % _always ;
76

    
77
##### SVN
78

    
79
add: _always
80
	$(call addDirWithIgnore,.,$$'')
81
	$(call addDirWithIgnore,src,$$'*')
82
	$(call addDirWithIgnore,maps,$$'.~*')
83
	$(call addDirWithIgnore,import,$$'*')
84
	$(call addDirWithIgnore,test,$$'*.out\n*.xml')
85
	$(call addDirWithIgnore,verify,$$'*.out')
86

    
87
##### Installation
88

    
89
reinstall: _always uninstall install ;
90

    
91
##### Maps
92

    
93
srcMap := maps/src.%.csv
94
fullViaMap := maps/%.full.csv
95
directMap := maps/VegBIEN.%.csv
96
allViaMaps := $(filter-out $(srcMap) $(fullViaMap) $(directMap),\
97
$(wildcard maps/*.csv))
98
via := $(firstword $(sort $(basename $(basename $(notdir $(allViaMaps))))))
99

    
100
coreMap := $(mappings)/$(via)-VegBIEN.%.csv
101
coreSelfMap := $(mappings)/$(via).self.%.csv
102
noEmptyMap := $(mappings)/$(via)-VegBIEN.%.no_empty.csv
103

    
104
viaMaps := $(wildcard $(tablesSort:%=maps/$(via).%.csv))
105
viaMaps += $(filter-out $(viaMaps) $(fullViaMap),$(wildcard maps/$(via).*.csv))
106

    
107
autogenMaps := $(subst $(via).,VegBIEN.,$(viaMaps))
108
directMaps := $(autogenMaps) $(filter-out $(autogenMaps),\
109
$(wildcard maps/VegBIEN.*.csv))
110
tables := $(directMaps:maps/VegBIEN.%.csv=%)
111

    
112
srcMaps := $(filter-out maps/src.join.%.csv,$(wildcard maps/src.*.csv))
113

    
114
# Must come before $(root)/% to override it
115
$(coreSelfMap): _always
116
	-+$(subMake)
117
# ignore errors if $(coreSelfMap) does not exist
118

    
119
# Via maps cleanup
120
ifneq ($(filter maps/.%.last_cleanup,$(MAKECMDGOALS)),)
121
maps/.$(via).%.csv.last_cleanup: maps/$(via).%.csv $(coreSelfMap)
122
	$(bin)/in_place $< $(bin)/subtract $(word 2,$+) 0 1
123
	touch $@
124
# default:
125
maps/.$(via).%.csv.last_cleanup: ;
126
else
127
$(viaMaps): _always
128
	$(selfMake) $(@:maps/%=maps/.%.last_cleanup)
129
endif
130

    
131
maps :=
132

    
133
joinSrcMap = $(if $(wildcard maps/src.$*.csv),$(bin)/in_place $@\
134
$(bin)/intersect maps/src.$*.csv 0)
135

    
136
makeFullCsv = $(if $(shell test -e $(word 2,$+) && echo t),\
137
env ignore=1 $(bin)/union <$+|$(bin)/sort_map >$@,$(CP) $< $@)
138
# can't use $(wildcard) because it won't recheck file after $(coreSelfMap) runs
139

    
140
maps/$(via).%.full.csv: maps/$(via).%.csv $(coreSelfMap)
141
	$(makeFullCsv)
142
	$(joinSrcMap)
143
maps += $(patsubst maps/%.csv,maps/%.full.csv,$(viaMaps))
144

    
145
maps/VegBIEN.%.csv: maps/$(via).%.full.csv $(coreMap)
146
	$(bin)/join <$+|$(bin)/sort_map >$@
147
maps += $(autogenMaps)
148

    
149
maps: $(maps) _always ;
150

    
151
all += $(maps)
152

    
153
##### External dependencies
154

    
155
$(root)/%: _always
156
	+$(subMake)
157

    
158
##### Mapping
159

    
160
dbExport := $(firstword $(wildcard src/db.*.sql))
161
inputFiles := $(wildcard $(exts:%=src/*.%))
162

    
163
+maps = $(filter maps/% $(mappings)/%,$(+_))
164
<in = $(firstword $(filter-out $(+maps),$(+_)))
165
srcs = $(sort $(wildcard $(exts:%=src/*.$*.%)))
166
map = $(if $(<in),<$(<in),\
167
$(if $(srcs),$(bin)/with_cat_csv $(srcs) --,\
168
$(if $(mapEnv),env $(mapEnv),\
169
$(error No input file src/*.$*.{$(exts)}))))\
170
$(root)/map $(+maps)
171
map2db = env out_database=vegbien $(map)
172

    
173
##### Import to VegBIEN
174

    
175
ifneq ($(dbExport)$(inputFiles),)
176

    
177
log_ = import/$*$(if $(n),.n=$(n),).$(date).log
178
trace = $(log_:.log=.trace)
179
import = -(set -x; "time" env commit=1\
180
$(if $(profile),profile_to=$(trace)) $(map2db)) $(if $(log),\
181
$(if $(n),,>>$(log_))) 2>&1$(if $(log),$(if $(n),|tee -a $(log_)))
182
# don't abort on import errors, which often relate to invalid input data
183

    
184
import: $(addprefix import-,$(tables)) _always ;
185

    
186
import-%: maps/VegBIEN.%.csv _always
187
	$(import)
188
# default:
189
import-%: _always ;
190

    
191
else
192
import: _always ;
193
endif
194

    
195
##### Log files from import
196

    
197
logs := $(wildcard import/*.log import/*.trace)
198

    
199
rm_logs: _always
200
	$(RM) $(logs)
201

    
202
##### Verification of import
203

    
204
verify: $(addprefix verify-,$(tables)) _always ;
205

    
206
verify-%: verify/%.ref verify/%.out _always
207
	-$(diffVerbose) $(+_)
208
# don't abort on verification errors, which are expected during development
209
# default:
210
verify-%: verify/%.out _always
211
	$(if $(shell test -e $< && echo t),cat $<)
212
# don't run if verify/%.out's default do-nothing action was used
213
# can't use $(wildcard) because it won't recheck file after verify/%.out is run
214

    
215
verify = $(if $(reverify),"time" $(psqlAsBien) --set=datasource="'$(datasrc)'"\
216
--no-align --field-separator=$$'\t' --pset=footer=off --pset=null=NULL <$< >$@)
217

    
218
verify/%.out: $(mappings)/verify.%.sql _always
219
	$(verify)
220
# default:
221
verify/%.out: _always ;
222

    
223
all += $(wildcard verify/*.out)
224

    
225
ifneq ($(dbExport),)
226
%.ref: %.ref.sql
227
	$(dbAsBien) $(db) <$< >$@
228
endif
229

    
230
##### Testing
231

    
232
ifneq ($(wildcard test/),)
233

    
234
hasOwnRef = $(filter-out %.2-step.xml,$@)
235
testRef = $(1:.2-step.xml=.xml).ref
236

    
237
define runTest
238
@echo "Testing $(abspath $@)..."
239
>$@ env test=1 n=$(test_n) $(1)
240
@(set -x; $(diff) $(call testRef,$@) $@) 2>&1 || { e=$$?;\
241
$(if $(wildcard $(call testRef,$@)),,cat $@;)\
242
$(if $(hasOwnRef),\
243
echo $(emph)"To accept new test output:"$(endEmph);\
244
echo "$(MAKE) $@-ok --directory=$(realpath .) --makefile=../input.Makefile";\
245
,\
246
echo $(emph)"Note: The preceding failed test is compared to another test's\
247
output"$(endEmph);\
248
echo $(emph)"When it fails, this always indicates a bug"$(endEmph);\
249
)\
250
exit $$e;}
251
endef
252

    
253
test2File = $(call runTest,$(map))
254

    
255
tests :=
256

    
257
test/$(via).%.xml: maps/$(via).%.full.csv _always
258
	$(test2File)
259
tests += test/$(via).%.xml
260

    
261
test/VegBIEN.%.xml: maps/VegBIEN.%.csv _always
262
	$(test2File)
263
tests += test/VegBIEN.%.xml
264

    
265
test/VegBIEN.%.2-step.xml: test/$(via).%.xml $(coreMap) _always
266
	-$(test2File)
267
# Don't abort tester if only 2-step test fails, as it's often finicky
268
tests += test/VegBIEN.%.2-step.xml
269

    
270
test/import.%.out: maps/VegBIEN.%.csv _always
271
	$(call runTest,$(map2db))
272
tests += test/import.%.out
273

    
274
testOutputs := $(foreach test,$(tests),$(tables:%=$(test)))
275

    
276
.PRECIOUS: $(testOutputs) # save outputs of failed tests so they can be accepted
277

    
278
test: _always $(testOutputs) ;
279

    
280
all += $(testOutputs)
281

    
282
# Accepts a test output: make <test_output_path>-ok
283
%-ok: _always
284
	$(CP) $* $(call testRef,$*)
285

    
286
else
287
test: _always ;
288
endif
289

    
290
##### Input-type-specific
291

    
292
# Each input type needs var $(mapEnv) and targets install, uninstall
293

    
294
#### DB export
295

    
296
ifneq ($(dbExport),)
297

    
298
dbEngineExt := $(subst .,,$(suffix $(basename $(notdir $(dbExport)))))
299
db := $(datasrc)
300

    
301
### Installation
302

    
303
install: _always db ;
304

    
305
uninstall: _always rm_db ;
306

    
307
### DB-engine-specific
308

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

    
311
ifeq ($(dbEngineExt),my)
312

    
313
dbEngine := MySQL
314

    
315
bienPassword := $(shell cat $(root)/config/bien_password)
316
mysqlAs = mysql --user=$(1) --password='$(bienPassword)'
317
mysqlAsRoot := $(call mysqlAs,root)
318
dbAsBien := $(call mysqlAs,bien)
319

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

    
322
define createDb
323
echo "CREATE DATABASE $(db) DEFAULT CHARACTER SET latin1;"|$(mysqlAsRoot)
324
-$(mysqlAsRoot) --database=$(db) <$<
325
endef
326
# ignore errors in db import so that GRANT will still be run
327

    
328
db: $(dbExport) _always
329
	$(if $(dbExists),,$(createDb))
330
	echo "GRANT SELECT ON $(db).* TO 'bien'@'localhost';"|$(mysqlAsRoot)
331

    
332
rm_db: _always
333
	-echo "REVOKE ALL ON $(db).* FROM 'bien'@'localhost';"|$(mysqlAsRoot)
334
	echo "DROP DATABASE IF EXISTS $(db);"|$(mysqlAsRoot)
335
# ignore errors if grant not defined
336

    
337
## Unrecognized DB engine
338

    
339
else
340
$(error The DB filename $(dbExport) must be db.my.sql)
341
endif
342

    
343
### Other input types
344

    
345
else
346

    
347
install: _always ;
348
uninstall: _always ;
349

    
350
endif
351

    
352
#### DB connection info
353

    
354
ifneq ($(dbEngine),)
355
# Must come after dbEngine is set
356
mapEnv := in_engine=$(dbEngine) in_database=$(db)
357
endif
(2-2/2)