1 /*
2 * $Id: make.i,v 1.2 2006-04-09 01:05:45 dhmunro Exp $
3 * create or update a Makefile for a yorick package (compiled extension)
4 */
5 /* Copyright (c) 2005, The Regents of the University of California.
6 * All rights reserved.
7 * This file is part of yorick (http://yorick.sourceforge.net).
8 * Read the accompanying LICENSE file for details.
9 */
10
11 func make(path, template=)
12 /* DOCUMENT make
13 * or make, path
14 * or make, path, template=1
15 * or make, path, template="Makefile_name"
16 * creates or updates the Makefile for a compiled package (plugin).
17 * If PATH is given, it is the directory containing the source code
18 * for the package; by default, PATH is ".", the current working
19 * directory.
20 *
21 * With the template= keyword, a template Makefile is written, which
22 * you can use as a starting point, filling in all the necessary
23 * information by hand. In this case, make makes no further checks
24 * on the contents of the directory. In the first form, the name of
25 * the Makefile template is "Makefile", but if the template= keyword
26 * is a string, that name is used instead. The template Makefile
27 * will not function at all until you edit it.
28 *
29 * Alternatively, without the template= keyword, make looks at the
30 * contents of the directory, and fills in as much of the Makefile
31 * as it can using what it sees. There are two cases:
32 * 1. A Makefile (or makefile) already exists. Provided the file
33 * contains a Y_MAKEDIR= line, make merely updates Y_MAKEDIR,
34 * Y_EXE, and Y_EXE_PKGS to reflect this running yorick, and
35 * attempts no other modifications of the existing Makefile.
36 * 2. No such Makefile exists. Then make writes a template Makefile,
37 * and reads all the .i interpreted source files, .c and .h C source
38 * files, and .f or .F or .m Fortran source files in the directory,
39 * filling in as much of the Makefile as possible. In simple cases,
40 * the resulting Makefile will be perfectly adequate. (If there
41 * is any Fortran source, however, it will never be adequate.)
42 *
43 * In case 2, the following steps are taken:
44 * A. Any .i files containing lines of the form
45 * plug_in, "pkname"
46 * or
47 * if (!is_void(plug_in)) plug_in, "pkgname"
48 * are identified as the "package include file(s)" which define
49 * the interpreted API for the package. The make function takes
50 * the name of the package from the "pkgname" in these lines.
51 * At least one .i file must contain at least one plug_in line;
52 * at most one "pkgname" must occur in all the plug_in lines in
53 * all the .i files in the directory. This information is used
54 * to fill in the PKG_NAME macro and the PKG_I macro in the
55 * Makefile (the latter becomes a list of every .i file containing
56 * a plug_in statement).
57 * B. All .c, .f, .F, or .m files are assumed to be required compiled
58 * source, and the OBJS macro in the Makefile is set to a list of
59 * all corresponding .o object files.
60 * C. If there are any .h C header files in the directory, every .c
61 * C source file is scanned for an include directive which reads
62 * the header. An approriate dependency line for each .o file which
63 * is discovered to depend on local .h files in this manner.
64 * These steps will be sufficient to produce a finished Makefile in
65 * many simple cases; an example is given in the extend/ directory of
66 * the yorick distribution (a compiled complementary error function).
67 *
68 * These same steps will be attempted for C++ source code. The gcc
69 * compiler recognizes the file extensions .cc, .cp, .cxx, .cpp, .c++,
70 * .C, and .CPP as C++ source code, and so does this make function.
71 * Similarly, .hh and .H are recognized as C++ header files, in addition
72 * to .h. Microsoft Visual C++ recognizes only the .cpp and .cxx
73 * extensions, and several other popular Windows compilers recognize
74 * only .cpp (which is the extension used by Trolltech's Qt library).
75 * All of this is by way of warning you to be prepared for portability
76 * problems if you attempt to use C++. The make function will emit
77 * rules for building C++ objects (cribbed from gmake); see the comments
78 * at that point in the Makefile if these don't work for you. Be sure
79 * that any C++ compiled functions which will be called by yorick are
80 * declared as extern "C" in your C++ source.
81 *
82 * However, it is impossible for yorick to automatically discover
83 * complicated Makefile rules your package may require. The most
84 * common example is dependency on a third party library, which
85 * requires a -l loader option, and very possibly a -L option as
86 * well. You need to add these flags to PKG_DEPLIBS in the Makefile
87 * by hand. If you want such a package to be buildable by non-experts,
88 * you will need to provide a configure script in order to fill in
89 * the required information across a wide variety of platforms. This
90 * can be exceptionally challenging, which is why yorick itself has
91 * no dependencies beyond what is guaranteed to be present on every
92 * system where it can run (libc, libm, and, on Unix systems, libX11).
93 *
94 * Once the Makefile has been built it becomes part of the source code
95 * of your package, and should not be removed. (You may want it to
96 * have an include directive to get the results of any configure script
97 * which must be run, however.) Thereafter, use
98 * yorick -batch make.i
99 * (or simply make in a running yorick)
100 * to set Y_MAKEDIR, Y_EXE, and Y_EXE_PKGS appropriately for the
101 * particular platform where you are building (case 1 above).
102 *
103 * Read Y_HOME/Makepkg for a description of the various make targets
104 * available. In a nutshell:
105 * make debug builds a debugging version (never a plugin)
106 * make builds a release version (a plugin if possible)
107 * make install installs the release version (may require root)
108 * make install2 installs to relocate subdirectory
109 * make clean removes all results, leaving only original source
110 *
111 * SEE ALSO: write_home, plug_in, autoload, cd
112 */
113 {
114 if (!path) path = ".";
115 else if (strglob("*/", path)) path = strpart(path, 1:-1);
116 files = lsdir(path);
117 path = path + "/";
118
119 mkfile = make_file;
120 make_subst, mkfile;
121 if (!is_void(template) && template) {
122 if (structof(template)==string && strlen(template)) name = template;
123 else name = "Makefile";
124 if (anyof(files == name)) {
125 for (i=0 ; ; i++) {
126 bakname = i? swrite(format=name+"-%ld.bak",i) : name+".bak";
127 if (noneof(files==bakname)) break;
128 }
129 rename, path+name, path+bakname;
130 }
131 write, create(path+name), format="%s\n", linesize=65535, mkfile;
132 write, path+name, format="created template %s\n";
133 return;
134 }
135
136 /* check for existing Makefile
137 * if present and not obsolete, just update Y_MAKEDIR, Y_EXE, and quit
138 * else move to .obs and proceed to create a new Makefile
139 */
140 name = anyof(files == "Makefile");
141 if (name || anyof(files == "makefile")) {
142 name = (name? "Makefile" : "makefile");
143 line = rdfile(path+name);
144 i = make_subst(line);
145 if (!numberof(i)) {
146 write, name, name, format=
147 "***WARNING*** %s is obsolete, moving to %s.obs\n";
148 if (anyof(files == name+".obs")) {
149 for (i=1 ; ; i++)
150 if (noneof(files==swrite(format=name+"-%ld.obs",i))) break;
151 rename, path+name+".obs", swrite(format=path+name+"-%ld.obs",i);
152 }
153 rename, path+name, path+name+".obs";
154 } else {
155 for (i=0 ; ; i++) {
156 bakname = i? swrite(format=name+"-%ld.bak",i) : name+".bak";
157 if (noneof(files==bakname)) break;
158 }
159 rename, path+name, path+bakname;
160 write, create(path+name), format="%s\n", linesize=65535, line;
161 remove, path+bakname;
162 write, path+name, format="updated %s\n";
163 return;
164 }
165 } else {
166 name = "Makefile";
167 }
168
169 /* look for plug_in command to identify package include files
170 * get the package name from any plug_in command(s) found, plus
171 * the PKG_I list
172 */
173 i = files(where(strglob("*.i", files)));
174 if (numberof(i)) i = i(where(i!="check.i"));
175 if (numberof(i)) {
176 ipkg = array(string, numberof(i));
177 for (k=1 ; k<=numberof(i) ; k++) {
178 line = open(path+i(k), "r", 1);
179 if (is_void(line)) continue;
180 line = rdfile(line);
181 list = strfind("plug_in", line);
182 line = line(where(list(2,) > 0));
183 if (numberof(line)) {
184 optif = "^[ \t]*(if[ \t]*\\(.+\\)[ \t]*)?";
185 list = strgrep(optif+"plug_in[ \t]*,[ \t]*[\"](.+)[\"]", line, sub=2);
186 line = strpart(line, list);
187 line = line(where(line));
188 if (!numberof(line) || anyof(line(1)!=line))
189 write, i(k), format=
190 "***WARNING*** skipping %s: multiple plug_in commands\n";
191 else
192 ipkg(k) = line(1);
193 /* could look for PROTOTYPE FORTRAN or EXTERNAL FORTRAN
194 * in order to judge if fortran code present, but this is
195 * not definitive since it could be called from C
196 */
197 }
198 }
199 list = where(ipkg);
200 i = i(list);
201 ipkg = ipkg(list);
202 if (numberof(i)) {
203 if (numberof(ipkg)>1 && anyof(ipkg(1)!=ipkg)) {
204 write, ipkg(1), format=
205 "***WARNING*** defining package %s, but plug_in commands\n";
206 write, sum(" "+ipkg(2:0)), format=
207 "***WARNING*** also use packages%s\n";
208 }
209 ipkg = ipkg(1);
210 }
211 }
212 if (!numberof(i))
213 error,
214 "***FATAL*** no plug_in command in any .i file in this directory";
215
216 mkfile(where(strglob("PKG_NAME=*",mkfile))) = pnm = "PKG_NAME="+ipkg;
217 mkfile(where(strglob("PKG_I=*",mkfile))) = inm =
218 "PKG_I="+strpart(sum(i+" "),1:-1);
219
220 /* look for #include statements in the .c files
221 * which refer to .h files in this directory,
222 * and generate .o file dependencies accordingly
223 */
224 c = files(where(strglob("*.c", files)));
225 if (numberof(c)) c = c(where((c!="yinit.c")&(c!="ywrap.c")));
226 cxx_list = [];
227 exts = numberof(c)? strpart(c, -1:0) : [];
228 for (k=1 ; k<=numberof(_make_cxx_exts) ; k++) {
229 list = where(strglob("*"+_make_cxx_exts(k), files));
230 if (numberof(list)) {
231 grow, cxx_list, [_make_cxx_exts(k)];
232 grow, exts, array(_make_cxx_exts(k), numberof(list));
233 grow, c, files(list);
234 }
235 }
236
237 h = files(where(strglob("*.h", files)));
238 o = strpart(c,1:-1) + "o";
239 if (numberof(cxx_list)) {
240 for (i=1 ; i<=numberof(_make_hxx_exts) ; i++) {
241 list = where(strglob("*"+_make_hxx_exts(i), files));
242 if (numberof(list)) grow, h, files(list);
243 }
244 len = strlen(exts);
245 list = where(len > 2);
246 for (k=1 ; k<=numberof(list) ; k++)
247 o(list(k)) = strpart(o(list(k)), 1:1-len(list(k))) + "o";
248 }
249 if (numberof(h)) {
250 h = h(sort(h));
251 hh = indgen(numberof(h));
252 if (numberof(c)) cdep = array(pointer, numberof(c));
253 for (k=1 ; k<=numberof(c) ; k++) {
254 line = open(path+c(k), "r", 1);
255 if (is_void(line)) continue;
256 line = rdfile(line);
257 list = strgrep("#[ \t]*include[ \t]*[<\"](.+)[\">]", line, sub=1);
258 line = strpart(line, list);
259 list = where(line);
260 if (!numberof(list)) continue;
261 line = line(list);
262 line = line(sort(line));
263 if (numberof(list) > 1) {
264 list = grow([string(0)], line);
265 line = line(where(list(2:0) != list(1:-1)));
266 }
267 ii = grow(array(0,numberof(line)), hh);
268 line = grow(line, h);
269 list = sort(line);
270 line = line(list);
271 ii = ii(list);
272 list = where(line(2:0) == line(1:-1));
273 if (!numberof(list)) continue;
274 cdep(k) = &h( max(ii(list), ii(list+1)) );
275 }
276 list = where(cdep);
277 chdep = c(list);
278 cdep = cdep(list);
279 if (numberof(chdep)) {
280 for (k=1 ; k<=numberof(chdep) ; k++)
281 chdep(k) = o(k) + ":" + sum(" "+(*cdep(k)));
282 }
283 mkfile = grow(mkfile(1:-1), chdep, [""], mkfile(0:0));
284 }
285
286 if (!no_fort) {
287 fort = files(where(strglob("*.[fFm]", files)));
288 if (numberof(fort) && !fortran_supported) {
289 write, format="***WARNING*** fortran source not fully supported%s","\n";
290 grow, exts, strpart(fort, -1:0);
291 grow, c, fort;
292 }
293 }
294 if (!numberof(c))
295 error,
296 "***FATAL*** no C, C++, or Fortran source files in this directory";
297
298 /* generate the OBJS= line, assuming all source files are
299 * to become part of the package
300 */
301 objnm = o+" ";
302 if (!dimsof(objnm)(1)) objnm = [objnm];
303 objs = array(string, numberof(objnm));
304 for (k=1 ; ; k++) {
305 lobjs = strlen(objnm)(psum);
306 j = max(sum(lobjs<70), 1);
307 objs(k) = objnm(sum:1:j);
308 if (j == numberof(objnm)) break;
309 objnm = objnm(j+1:0);
310 }
311 objs = objs(1:k);
312 objs(1) = "OBJS="+objs(1);
313 if (k > 1) {
314 objs(2:k) = " "+objs(2:k);
315 objs(1:k-1) += "\\";
316 }
317 objs(0) = strpart(objs(0),1:-1);
318
319 k = where(strglob("OBJS=*", mkfile))(1);
320 mkfile = grow(mkfile(1:k-1), objs, mkfile(k+1:0));
321
322 if (numberof(cxx_list)) {
323 cxx_lines = _make_cxx_lines(1:-1);
324 grow, cxx_lines, swrite(format=_make_cxx_rule, cxx_list);
325 grow, cxx_lines, _make_cxx_lines(0:0);
326 k = where(strglob("Y_SITE=*", mkfile))(1);
327 mkfile = grow(mkfile(1:k+1), cxx_lines, mkfile(k+1:0));
328 }
329
330 write, create(path+name), format="%s\n", linesize=65535, mkfile;
331 write, ((path!="./")?path:"")+name, format="created %s\n";
332 write, "automatically generated make macros and dependencies:";
333 write, format="%s\n", pnm;
334 write, format="%s\n", inm;
335 write, format="%s\n", objs;
336 if (!is_void(chdep)) write, format="%s\n", chdep;
337 write, "edit "+name+" by hand to provide PKG_DEPLIBS or other changes";
338 }
339
340 /* default C++ source extensions recognized by make() */
341 _make_cxx_exts = [".cxx", ".cpp", ".cc", ".c++", ".C", ".CPP"];
342 _make_hxx_exts = [".hh", ".hxx", ".H"]; /* .h also checked */
343 _make_cxx_rule =
344 "%s.o:\n\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<";
345 _make_cxx_lines =
346 ["# ------------begin C++ source hacks",
347 "# must use C++ to load yorick with this C++ package",
348 "# this assumes make default CXX macro points to C++ compiler",
349 "CXXFLAGS=$(CFLAGS)",
350 "LD_DLL=$(CXX) $(LDFLAGS) $(PLUG_SHARED)",
351 "LD_EXE=$(CXX) $(LDFLAGS) $(PLUG_EXPORT)",
352 "",
353 "# C++ has no standard file extension, supply default make rule(s)",
354 "# --------------end C++ source hacks"
355 ];
356 /* typical standalone link rule
357 LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
358 .cc:
359 $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@
360 */
361
362 func make_subst(&line)
363 {
364 i = where(strglob("Y_MAKEDIR=*", line));
365 if (numberof(i)) {
366 j = i(1);
367 i = where(strglob("Y_EXE=*", line));
368 if (numberof(i)) {
369 i = i(1);
370 } else { /* file probably corrupt */
371 i = j+1;
372 line = grow(line(1:j), string(0), line(i:0));
373 }
374 k = where(strglob("Y_EXE_PKGS=*", line));
375 if (numberof(k)) {
376 k = k(1);
377 } else { /* file probably corrupt */
378 k = i+1;
379 line = grow(line(1:i), string(0), line(k:0));
380 }
381 pkgs = get_pkgnames(0);
382 pkgs = pkgs(where(pkgs!="yor"));
383 if (!numberof(pkgs)) pkgs = "";
384 else pkgs = strpart(sum(pkgs+" "), 1:-1);
385 exename = get_argv()(1);
386 if (strglob("*.EXE", exename)) strcase,0, exename;
387 line(j) = "Y_MAKEDIR=" + make_despace(strpart(Y_HOME,1:-1));
388 line(i) = "Y_EXE=" + make_despace(exename);
389 line(k) = "Y_EXE_PKGS=" + pkgs;
390 i = k;
391 k = where(strglob("Y_EXE_HOME=*", line));
392 if (numberof(k)) {
393 k = k(1);
394 } else { /* file probably corrupt */
395 k = i+1;
396 line = grow(line(1:i), string(0), line(k:0));
397 }
398 line(k) = "Y_EXE_HOME=" + make_despace(strpart(Y_HOME,1:-1));
399 i = k;
400 k = where(strglob("Y_EXE_SITE=*", line));
401 if (numberof(k)) {
402 k = k(1);
403 } else { /* file probably corrupt */
404 k = i+1;
405 line = grow(line(1:i), string(0), line(k:0));
406 }
407 line(k) = "Y_EXE_SITE=" + make_despace(strpart(Y_SITE,1:-1));
408 i = k;
409 k = where(strglob("Y_HOME_PKG=*", line));
410 if (numberof(k)) {
411 k = k(1);
412 } else { /* file probably corrupt */
413 k = i+1;
414 line = grow(line(1:i), string(0), line(k:0));
415 }
416 y = Y_HOME_PKG? make_despace(strpart(Y_HOME_PKG,1:-1)) : "";
417 line(k) = "Y_HOME_PKG=" + y;
418 }
419 return i;
420 }
421
make_despace(name)422 func make_despace(name)
423 {
424 /* backspace escape blanks in path names */
425 return streplace(name, strfind(" ",name,n=256), "\\ ");
426 }
427
428 /* here is the yorick package Makefile template
429 * fill in PKG_NAME, PKG_I, OBJS, and optionally
430 * PKG_DEPLIBS, PKG_CFLAGS, EXTRA_PKGS, PKG_CLEAN, etc.
431 *
432 * yorick -batch make.i
433 * fills in Y_MAKEDIR, Y_EXE
434 * or creates a Makefile if none is present
435 */
436 make_file =
437 ["# these values filled in by yorick -batch make.i",
438 "Y_MAKEDIR=### Y_HOME (directory containing Makepkg)",
439 "Y_EXE=### Y_LAUNCH/yorick (path to yorick executable)",
440 "Y_EXE_PKGS=### packages statically loaded into Y_EXE",
441 "Y_EXE_HOME=### Y_HOME for Y_EXE",
442 "Y_EXE_SITE=### Y_SITE for Y_EXE",
443 "Y_HOME_PKG=### Y_HOME_PKG for Y_EXE",
444 "",
445 "# ----------------------------------------------------- optimization flags",
446 "",
447 "# options for make command line, e.g.- make COPT=-g TGT=exe",
448 "COPT=$(COPT_DEFAULT)",
449 "TGT=$(DEFAULT_TGT)",
450 "",
451 "# ------------------------------------------------ macros for this package",
452 "",
453 "PKG_NAME=### name of this package",
454 "PKG_I=### pkg.i file(s) defining interpreted API for this package",
455 "",
456 "OBJS=### list of .o files (library modules) for this package",
457 "",
458 "# change to give the executable a name other than yorick",
459 "PKG_EXENAME=yorick",
460 "",
461 "# PKG_DEPLIBS=-Lsomedir -lsomelib for dependencies of this package",
462 "PKG_DEPLIBS=",
463 "# set compiler (or rarely loader) flags specific to this package",
464 "PKG_CFLAGS=",
465 "PKG_LDFLAGS=",
466 "",
467 "# list of additional package names you want in PKG_EXENAME",
468 "# (typically $(Y_EXE_PKGS) should be first here)",
469 "EXTRA_PKGS=$(Y_EXE_PKGS)",
470 "",
471 "# list of additional files for clean",
472 "PKG_CLEAN=",
473 "",
474 "# autoload file for this package, if any",
475 "PKG_I_START=",
476 "# non-pkg.i include files for this package, if any",
477 "PKG_I_EXTRA=",
478 "",
479 "# -------------------------------- standard targets and rules (in Makepkg)",
480 "",
481 "# set macros Makepkg uses in target and dependency names",
482 "# DLL_TARGETS, LIB_TARGETS, EXE_TARGETS",
483 "# are any additional targets (defined below) prerequisite to",
484 "# the plugin library, archive library, and executable, respectively",
485 "PKG_I_DEPS=$(PKG_I)",
486 "Y_DISTMAKE=distmake",
487 "",
488 "include $(Y_MAKEDIR)/Make.cfg",
489 "include $(Y_MAKEDIR)/Makepkg",
490 "include $(Y_MAKEDIR)/Make$(TGT)",
491 "",
492 "# override macros Makepkg sets for rules and other macros",
493 "# see comments in Y_HOME/Makepkg for a list of possibilities",
494 "",
495 "# if this package built with mpy: 1. be sure mpy appears in EXTRA_PKGS,",
496 "# 2. set TGT=exe, and 3. uncomment following two lines",
497 "# Y_MAIN_O=$(Y_LIBEXE)/mpymain.o",
498 "# include $(Y_MAKEDIR)/Makempy",
499 "",
500 "# configure script for this package may produce make macros:",
501 "# include output-makefile-from-package-configure",
502 "",
503 "# reduce chance of yorick-1.5 corrupting this Makefile",
504 "MAKE_TEMPLATE = protect-against-1.5",
505 "",
506 "# ------------------------------------- targets and rules for this package",
507 "",
508 "# simple example:",
509 "#myfunc.o: myapi.h",
510 "# more complex example (also consider using PKG_CFLAGS above):",
511 "#myfunc.o: myapi.h myfunc.c",
512 "# $(CC) $(CPPFLAGS) $(CFLAGS) -DMY_SWITCH -o $@ -c myfunc.c",
513 "",
514 "# -------------------------------------------------------- end of Makefile"
515 ];
516
517 if (batch()) {
518 if (numberof(get_argv()) > 1)
519 error, "yorick -batch make.i no longer accepts any parameters";
520 make;
521 quit;
522 }
523
write_home(dir)524 func write_home(dir)
525 /* DOCUMENT write_home, dir
526 writes file Y_HOME.txt to directory DIR (current working directory
527 by default). This file contains the path of Y_HOME, used by yorick
528 to find its public installation when started as a private pacakge.
529 */
530 {
531 if (!dir) dir = "";
532 else if (strpart(dir,0:0) != "/") dir += "/";
533 write,create(dir+"Y_HOME.txt"),format="%s\n", Y_HOME;
534 }
535