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