-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
296 lines (261 loc) · 9.87 KB
/
Makefile
File metadata and controls
296 lines (261 loc) · 9.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
TITLE = COSMO tasks
VERSION = $(shell sed -n "s/.*VERSION *= *['\"]\(.*\)['\"].*/\1/p" setup.py)
HOMEPAGE = $(shell sed -n "s/.*PROJECTHOME *= *['\"]\(.*\)['\"].*/\1/p" setup.py)
LOGDIR = log
SHELL = bash
# this might be 'python2' on some systems like Ubuntu LTS; if that's the case,
# it's best to just create a virtualenv using that Python, then activate it
PYTHON = python
# how many dinuc-shuffled scans to run; specify in the environment to override
BGSCANS = 3
# where to 'make install' to
# see also 'MODULEDESTROOT', 'MODULEFILEDEST', and the 'module:' target, below
PREFIX = /usr/local
# ANSI terminal colors (see 'man tput').
# Don't set these if there isn't a $TERM environment variable
# source: https://linuxtidbits.wordpress.com/2008/08/11/output-color-on-bash-scripts/
ifneq ($(strip $(TERM)),)
BLD := $(shell tput bold)
UL := $(shell tput sgr 0 1)
RED := $(shell tput setaf 1)
GRN := $(shell tput setaf 2)
YEL := $(shell tput setaf 3)
BLU := $(shell tput setaf 4)
MAG := $(shell tput setaf 5)
CYA := $(shell tput setaf 6)
RST := $(shell tput sgr0)
ERR := $(BLD)$(RED)
WRN := $(BLD)$(YEL)
OK := $(BLD)$(GRN)
endif
ERROR := [$(ERR)ERROR$(RST)]
WARN := [$(WRN)WARNING$(RST)]
HINT := [$(MAG)$(BLD)HINT$(RST)]
NOTE := [$(WRN)NOTE$(RST)]
INFO := [$(CYA)INFO$(RST)]
help: # print this help
@$(PYTHON) -c "$$AUTOGEN_HELP_PY" "$(firstword $(MAKEFILE_LIST))"
# define (and export) CLEAN=1 in the environment or pass it on the `make`
# command line to *not* ask to clean up test results; instead, just do it
export CLEAN
test: cosmo.coords.bed cosmo.counts.tab stats.tab # run a basic test suite on COSMO
@echo
# $(BLD)Comparing COSMO outputs to known good in 'examples/output'…$(RST)
@for f in $^; do \
if [[ ! -s $$f ]]; then \
echo -e "\n$(ERROR) $$f is empty! Try 'make clean' to start over.\n" >&2; \
exit 1; \
fi; \
echo -n "- $$f vs. examples/output/$$f… " >&2; \
( set -o pipefail; diff $$f examples/output/$$f | head ); \
if (( $$? == 0 )); then \
echo "$(OK)OK$(RST)" >&2; \
else \
echo "$(ERR)FAIL$(RST)" >&2; \
failed=$$(( failed + 1 )); \
fi; \
done; \
if (( failed )); then exit 1; fi
@if [[ -z $$CLEAN ]]; then \
read -p $$'\nClean results from test run now? [Y/n] ' CLEAN; \
fi; \
if [[ -z $$CLEAN || $$CLEAN =~ ^[Yy] ]]; then \
make clean || exit 1; \
else \
echo -e "\nOK, preserving outputs from test run."; \
echo -e "Run 'make clean' to clean them up later.\n"; \
fi; \
EXAMPLEFASTA = examples/example.40k.fa
cosmo.coords.bed: $(EXAMPLEFASTA)
./cosmo.py -p examples/jpwm -fa $< -C
cosmo.counts.tab: $(EXAMPLEFASTA)
./cosmo.py -p examples/jpwm -fa $<
bgscans = $(shell echo cosmo.counts.tab.{1..$(BGSCANS)})
stats.tab: cosmo.counts.tab $(bgscans)
./cosmostats.py > $@
@if [[ ! -s $@ ]]; then \
echo -e "\n$(ERROR) Output file '$@' was empty. Can't continue.\n" >&2; \
rm $@; \
exit 1; \
fi
# for testing only; RANDSEED is incremented by the shuffle run number,
# otherwise the counts from each of the background scans will be the same
RANDSEED = 1000
cosmo.counts.tab.%: $(EXAMPLEFASTA)
./cosmo.py -p examples/jpwm -fa $< -N $* --random-seed $$(( $(RANDSEED) + $* ))
examples/example%.fa:
gunzip -dc $@.gz > $@
examples/example.fa:
gunzip -dc $@.gz > $@
# locate a Python library
pywhich = $(shell $(PYTHON) -c 'm = __import__("$(1)", globals(), locals(), [], 0); print m.__file__' 2>/dev/null)
# print the major.minor.patchlevel version of Python
pyver := $(shell $(PYTHON) -c 'import sys; print("%d.%d.%d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro))')
# returns 0 if Python module $(1) can be loaded
pyhavemod = $(PYTHON) -c 'import $(1)' 2>/dev/null
pysyspath = $(shell $(PYTHON) -c 'import sys; print(sys.path)')
deps: have-python-27 have-pip moods # install COSMO and its dependencies
@echo
# $(BLD)Checking for COSMO's dependencies…$(RST)
@if $(call pyhavemod,numpy); then \
echo "$(INFO) Found numpy at '$(call pywhich,numpy)" >&2; \
else \
echo "$(INFO) No numpy module present in sys.path: $(call pysyspath)" >&2; \
echo -e "\n# $(BLD)Installing COSMO's dependencies…$(RST)" >&2; \
$(PYTHON) -m pip install -r requirements.txt || exit 1; \
fi
moods: have-cloned-moods-submodule have-python-27 have-pip moods-lib # build MOODS 1.x C library and install the Python library
@echo
# $(BLD)Checking for MOODS Python module…$(RST)
@if $(call pyhavemod,MOODS); then \
echo "$(INFO) Found MOODS at '$(call pywhich,MOODS)" >&2; \
else \
echo "$(INFO) No MOODS module present in sys.path: $(call pysyspath)" >&2; \
echo -e "\n# $(BLD)Building MOODS Python module…$(RST)" >&2; \
: very old versions of 'pip' might fail here; \
cd MOODS/python && $(PYTHON) setup.py install || exit 1; \
fi
have-cloned-moods-submodule:
@echo
# $(BLD)checking if user did 'git clone --recursive'…$(RST)
@if [[ -d MOODS/src ]]; then \
echo "$(INFO) MOODS/src subdirectory exists" >&2; \
else \
echo "$(ERROR): MOODS submodule missing" >&2; \
echo "Please run 'git submodule init && git submodule update' and try again." >&2; \
exit 1; \
fi
moods-lib: MOODS/src/libpssm.a
MOODS/src/libpssm.a:
@echo
# $(BLD)Building MOODS C library…$(RST)
cd MOODS/src && make
have-python-27:
@echo
# $(BLD)Checking for Python 2.7.x…$(RST)
@if [[ "$(pyver)" == 2.7.* ]]; then \
echo "$(INFO) Found Python v$(pyver)" >&2; \
else \
echo -e "\n$(ERROR) Python interpreter missing or not required version 2.7.x.\n" >&2; \
exit 1; \
fi
have-pip:
@echo
# $(BLD)Checking for pip…$(RST)
@if $(call pyhavemod,pip); then \
echo "$(INFO) Found pip at $(call pywhich,pip)" >&2; \
else \
echo -e "\n$(ERROR): No pip found for the current Python interpreter." >&2; \
echo -e " Maybe you need to create/activate a virtualenv? See the README.\n" >&2; \
exit 1; \
fi
install: have-python-27 moods-lib # [install] install MOODS and COSMO to /usr/local [override with PREFIX=]
@echo
# installing the MOODS library
cd MOODS/python && $(PYTHON) setup.py install --prefix="$(PREFIX)"
@# for some reason, PYTHONPATH has to be defined here, but not for MOODS
@# I think it's because COSMO installs scripts/entrypoints? ¯\_(ツ)_/¯
# installing COSMO itself
PYTHONPATH="$(PREFIX)/lib/python2.7/site-packages" python setup.py install --prefix="$(PREFIX)"
# symlink 'cosmo' to 'cosmo.py' and likewise for 'cosmostats.py'
cd $(PREFIX)/bin && \
ln -sf cosmo.py cosmo && \
ln -sf cosmostats.py cosmostats
# copying example PWMs
mkdir -p $(PREFIX)/lib/cosmo
cp -r examples/jpwm $(PREFIX)/lib/cosmo
# where to install COSMO as a module, and where to put the modulefile
MODULEDESTROOT = $(if $(LABLOCALMODULES),$(LABLOCALMODULES),$(PREFIX)/modules)
MODULEFILEDEST = $(MODULEDESTROOT)/modulefiles/cosmo
# update this as appropriate for your local setup
MODULEPYTHON27MOD = python/2.7.18-wrl
MODULEVERSION = $(VERSION)
MODULEHOMEPAGE = $(HOMEPAGE)
# set to an empty string to *not* ask to set the new modulefile as the default
ASKDEFAULTMODULEVER = 1
module: modulefile # [install] install COSMO as an Environment Modules module
@# dummy check, because I would tend to do this…
@if which $(PYTHON) 2>/dev/null | grep -qE 'v?env/bin'; then \
echo -e "\nYou should deactivate the virtualenv before running this step:" >&2; \
echo -e "\n $$ deactivate" >&2; \
echo -e "\nThen try running 'make $@' again." >&2; \
exit 1; \
fi
@echo
# installing the COSMO modulefile
install -m644 modulefile/modulefile.tcl $(MODULEFILEDEST)/$(VERSION)
ifneq ($(ASKDEFAULTMODULEVER),)
@read -p $$'\nSet version $(VERSION) as the new default module? [y/N] '; \
if [[ $$REPLY =~ ^[Yy] ]]; then \
install -m644 modulefile/dot-version.tcl $(MODULEFILEDEST)/.version; \
echo; \
fi
endif
@# installing MOODS and COSMO
mkdir -p $(MODULEDESTROOT)/cosmo/$(VERSION)
make install PREFIX=$(MODULEDESTROOT)/cosmo/$(VERSION)
# note that this will catch a few of Environment Modules *own* variables, too…
M4DEFS = $(foreach V,$(filter MODULE%,$(.VARIABLES)),-D $V='$($V)')
modulefile: modulefile/modulefile.tcl modulefile/dot-version.tcl # update the Environment Modules modulefile
modulefile/%: modulefile/%.m4
@echo
# generating the COSMO Environment Modules modulefile
m4 -P $(M4DEFS) $< > $@
clean: # [clean] remove COSMO output data (*.bed, *.tab*, *.fa)
-rm *.bed *.tab* *.log
-rm examples/*.bed examples/*.tab* examples/*.log
-rm examples/example*.fa
distclean: clean # [clean] clean + remove intermediate build artifacts
-cd MOODS/src && make clean
-rm -r MOODS/python/build
-rm -r build dist *.egg-info
-find . -name "*.pyc" -delete
envclean: # [clean] remove the virtualenv (assuming you named it 'venv')
-rm -r venv
@echo >&2; \
echo "$(NOTE) Run 'deactivate' to deactivate the Python virtualenv." >&2
.PHONY: clean
##
## internals you can safely ignore
##
# automatically generate 'make help' given the name of the Makefile
define AUTOGEN_HELP_PY
from __future__ import print_function
import re, sys
def esc(code):
return '\033[%sm' % code
max = 0
groups = {}
targets = []
grouplist = []
print("\n %sMakefile targets - %s v%s%s\n" %
(esc('0;4'), "$(TITLE)", "$(VERSION)", esc(0)))
with open(sys.argv[1], 'r') as makefile:
for line in makefile:
groupmatch = re.match(r'^(\w+):.*?# +\[(\w+)\] +(.*)$$', line)
match = re.match(r'^(\w+):.*?# +(.*)$$', line)
if groupmatch:
target, group, help = groupmatch.groups()
if len(target) > max:
max = len(target)
if not groups.get(group):
grouplist.append(group)
groups[group] = []
groups[group].append((target, help))
elif match:
target, help = match.groups()
if len(target) > max:
max = len(target)
targets.append((target, help))
fmt = ' %smake %-' + str(max) + 's%s %s'
if targets:
for t in targets:
print(fmt % (esc('1;34'), t[0], esc(0), t[1]))
if groups:
for g in grouplist:
print('\n [%s]' % g)
for t in groups[g]:
print(fmt % (esc('1;34'), t[0], esc(0), t[1]))
print("\n Homepage: %s%s%s\n" % (esc('0;36'), "$(HOMEPAGE)", esc(0)))
endef
export AUTOGEN_HELP_PY