Home
Manual
Packages
Global Index
Keywords
Quick Reference
|
/*
MAKE.I
Yorick automatic Makefile generator.
$Id$
*/
/* Copyright (c) 1996. The Regents of the University of California.
All rights reserved. */
func make (args)
/* DOCUMENT make, args
or make
makes a Makefile for ARGS(1) in the current working directory.
args is a list of strings that looks like this:
[code_name, new1, new2, ..., "+", old1, old2, ...]
(Without ARGS, checks to be sure that existing Makefile has
correct MAKE_TEMPLATE for this platform.)
Each NEWi is a Yorick include file in the current working
directory, which will become a startup file for the new custom
version of Yorick you want to build. Each OLDi file is a
startup file for a previously built package; these must be in
either the Y_SITE/i0 or Y_SITE/contrib directories (or
have softlinks in the current working directory).
The matrix.i and fft.i packages are included by default; if you
want to override that default, specify "-matrix" and/or "-fft"
as part of the list of OLDi.
All the startup files must contain a MAKE-INSTRUCTIONS comment.
It should be placed near the top of each startup file.
This comment consists of a line containing slash* followed by
MAKE-INSTRUCTIONS then a number of lines with various keywords,
then the closing *slash on its own line. The recognized keywords
are:
SRCS = src1.c src2.c src3.f ...
LIB = pkg
DEPLIBS = dep1 dep2
NO-WRAPPERS
The SRCS keyword is mandatory; the others are all optional.
The SRCS are the source files which produce the compiled functions
referenced by the extern statements in the startup file which
contains this MAKE-INSTRUCTIONS comment. The extension ".f" or
".F" implies Fortran source code, which has some special configuration
fiddles (see Y_HOME/Maketmpl). You can continue the list by placing
a backslash \ at the end of a line; the list of SRCS stops after the
first line which does not end with \.
The LIB keyword specifies the name of the library for this package.
The library will actually be called libpkg.a and the load option will
be -lpkg (don't try to include these decoration in the name pkg you
supply). If the LIB keyword is not present, you will get no library,
and you won't be able to use this startup file as one of the OLDi in
any new custom versions of Yorick you may build in the future.
DEPLIBS is a list of any dependent libraries which the compiled
functions in SRCS may need. Do not include m (the libm math library)
or any Fortran libraries or other libraries related to or used by
Yorick itself here. Hopefully, you won't need DEPLIBS at all -- if
you do, you will probably need to tweek the Makefile to add correct
-L options so that libdep1, libdep2, etc can be found.
If none of the extern functions in this startup file has a
PROTOTYPE comment, add the NO-WRAPPERS keyword, otherwise don't.
See Y_HOME/Maketmpl for more information (start yorick and type
Y_HOME to find the directory name at your site).
To run this program in batch mode:
yorick -batch make.i
type this after you've moved to a new platform in order to
set the MAKE_TEMPLATE in the Makefile to the correct value
for this platform (Y_HOME is probably different)
yorick -batch make.i code_name new1 new2 ...
yorick -batch make.i code_name new1 new2 ... + old1 old2 ...
type this to construct a new Makefile for code_name
if a Makefile already exists, it simply performs the check as
above; if you want to rebuild a new Makefile, you need to
remove or change the name of the old one by hand
*/
{
if (check_makefile()) return;
if (numberof(args)<1)
error, "Usage: code_name new1 new2 ... + old1 old2 ...";
code_name= args(1);
if (numberof(args)<2) args= [];
else args= args(2:0);
if (numberof(args)) {
list= where(args=="+");
if (numberof(list)) {
list= list(1);
if (list>1) new_pkgs= args(1:list-1);
else new_pkgs= [];
if (list<numberof(args)) old_pkgs= args(list+1:0);
else old_pkgs= [];
} else {
new_pkgs= args;
old_pkgs= [];
}
}
dflt_pkgs= [];
list= where(old_pkgs!="-matrix");
if (numberof(list)<numberof(old_pkgs)) old_pkgs= old_pkgs(list);
else grow, dflt_pkgs, "matrix.i";
list= where(old_pkgs!="-fft");
if (numberof(list)<numberof(old_pkgs)) old_pkgs= old_pkgs(list);
else grow, dflt_pkgs, "fft.i";
old_pkgs= undup_names(grow(old_pkgs, dflt_pkgs));
new_makefile, code_name, new_pkgs, old_pkgs;
}
func check_makefile (void)
{
/* check an existing makefile for MAKE_TEMPLATE
* also, check for an outdated Makefile and try to update to
* the modern form
* returns 1 if (possibly modified) Makefile is usable,
* returns 0 if new Makefile must be built from scratch */
f= open("Makefile", "r", 1);
if (is_void(f)) return 0;
/* read the whole Makefile into memory as an array of strings */
list= array(pointer, 200);
len= array(0, 200);
obsolete= [];
i0= 0;
for (i=1 ; i<=200 ; ++i) {
lines= rdline(f, 100);
if (is_void(obsolete)) {
token= strtok(lines," \t=");
test= where(token(1,)=="MAKE_TEMPLATE");
if (numberof(test)) {
obsolete= 0;
test= test(1); /* should there be a warning if more than one? */
token= strtok(token(2,test)," \t=")(1);
n= -strlen(token);
for (j=-1 ; j>n ; --j) if (strpart(token,j:j)=="/") break;
if (strpart(token,1:j)==Y_HOME) return 1;
lines(test)= "MAKE_TEMPLATE = "+Y_HOME+strpart(token,j+1:0);
} else {
token= token(1,);
test= where((token=="exec_prefix") | (token=="Y_HOME"));
if (numberof(test)) {
obsolete= sum(len)+test(1);
lines(test)= "#"+lines(test);
i0= i;
}
}
}
if (obsolete) {
if (i0!=i) {
token= strtok(lines," \t=")(1,);
test= where((token=="exec_prefix") | (token=="Y_HOME"));
if (numberof(test)) lines(test)= "#"+lines(test);
}
test= where(strmatch(lines,"-END-CODE-SPECIFIC-SECTION-"));
if (numberof(test)) {
test= test(1);
if (test<3) {
if (i>1) {
list(i-1)= (*list(i-1))(1:-1);
lines= [string(0)];
} else {
/* this is not really a Makefile at all */
close, f;
rename, "Makefile", "Makefile.old";
return 0;
}
} else {
lines= lines(1:test-2);
}
/* since lines shortened, this will be final pass */
}
}
lines= lines(where(lines));
list(i)= &lines;
len(i)= numberof(lines);
if (numberof(lines)<100) break;
}
n= sum(len);
lines= n? array(string, n) : [];
for (i=1,j=0 ; j<n ; j+=len(i++)) {
if (!len(i)) continue;
lines(j+1:j+len(i))= *list(i);
}
list= len= [];
/* Makefile usable, but needs to be modified */
close, f;
rename, "Makefile", "Makefile.old";
if (obsolete) {
lines2= lines(obsolete:0);
if (obsolete>1) lines= lines(1:obsolete-1);
else lines= [];
grow, lines, ["MAKE_TEMPLATE = "+Y_HOME+"Maketmpl"],
lines2, ["# to set MAKE_TEMPLATE properly, run yorick -batch make.i",
"include $(MAKE_TEMPLATE)"];
}
write, create("Makefile"), format="%s\n", lines;
return 1;
}
func new_makefile (code_name, new_pkgs, old_pkgs)
{
n= numberof(new_pkgs);
if (!n) {
new_pkgs= code_name+".i";
n= 1;
}
srcs= lib= deplibs= s= d= [];
nowrap= 0;
for (i=1 ; i<=n ; ++i) {
f= open(new_pkgs(i), "r", 1);
if (is_void(f)) { f=new_pkgs(i); goto oops; }
instrucs= get_keys(new_pkgs(i), f, s, l, d);
if (is_void(lib))
lib= l;
else if (!is_void(l) && anyof(lib!=l))
error, "new pacakges must all specify same LIB in MAKE-INSTRUCTIONS";
grow, srcs, s;
grow, deplibs, d;
list= where(instrucs(1,)=="NO-WRAPPERS");
if (numberof(list)) nowrap++;
}
deplibs= grow(lib, deplibs);
if (numberof(lib)>1)
error, "new packages may specify at most one LIB in MAKE-INSTRUCTIONS";
else if (numberof(lib)==1)
lib= lib(1);
code_library= lib;
nsrcs= numberof(srcs);
if (nowrap==n) nowrap= "";
else nowrap= " ywrap.o";
n= numberof(old_pkgs);
for (i=1 ; i<=n ; ++i) {
/* note: . is assumed to be Y_LAUNCH */
f= find_file(old_pkgs(i),
["./",Y_SITE+"i0/",Y_SITE+"contrib/"]);
if (is_void(f)) { f=old_pkgs(i); goto oops; }
get_keys, old_pkgs(i), f, s, l, d;
if (is_void(l) && old_pkgs(i)!="matrix.i" && old_pkgs(i)!="fft.i")
error,"package "+old_pkgs(i)+" specifies no LIB in MAKE-INSTRUCTIONS";
grow, srcs, s;
grow, deplibs, l, d;
}
srcs= strtok(srcs,".");
ext= srcs(2,);
srcs= srcs(1,);
list= where((ext=="f") | (ext=="F"));
fortran= (numberof(list)!=0);
objs= srcs(1:nsrcs)+".o";
if (!is_void(deplibs))
deplibs= "-l"+undup_names(deplibs);
if (open("Makefile","r",1)) rename, "Makefile", "Makefile.old";
f= create("Makefile");
write,f,format="# Makefile for %s\n",code_name;
write,f,format="# generated by make.i %s\n\n",timestamp();
write,f,format="%s\n",
"######################################################################";
write,f,format="# %s\n\n","First section is definitions for Maketmpl";
write,f,format="MAKE_TEMPLATE = %s%s\n\n",Y_HOME,"Maketmpl";
write,f,format="C_OPTIMIZE = %s\nLD_OPTIMIZE = $(C_OPTIMIZE)\n","-O";
if (fortran) write,f,format="F_OPTIMIZE = %s\n","-O";
if (!is_void(code_library)) {
write,f,format="\nCODE_NAME = %s\n", code_name;
write,f,format="CODE_LIBRARY = lib%s.a\n", code_library;
write,f,format="NON_SHARABLE = %s\n", "unused";
} else {
write,f,format="\nNON_SHARABLE = %s\n", code_name;
write,f,format="CODE_NAME = %s\n", "unused";
write,f,format="CODE_LIBRARY = %s\n", "unused2";
}
write,f,format="YWRAP_O =%s\n\n",nowrap;
objlist= ["OBJS ="];
tot= strlen(objlist(1));
for (i=i0=1 ; i<=numberof(objs) ; ++i) {
obj= objs(i);
len= strlen(obj);
if (tot+len+1 > 70) {
objlist(i0)+= " \\";
grow, objlist, [" "];
tot= strlen(objlist(0));
++i0;
}
objlist(i0)+= " "+obj;
tot+= len+1;
}
write,f,format="%s",objlist;
lib= "";
for (i=1 ; i<=numberof(deplibs) ; ++i) lib+= " "+deplibs(i);
write,f,format="\nPKG_LIBS =%s\n",lib;
if (is_void(code_library))
write,f,format="PKG_OBJS = %s\n","$(OBJS) $(YWRAP_O)"
pkg= "";
for (i=1 ; i<=numberof(new_pkgs) ; ++i) pkg+= " "+new_pkgs(i);
write,f,format="\nY_INCLUDE =%s\n",pkg;
pkg= "";
for (i=1 ; i<=numberof(old_pkgs) ; ++i) pkg+= " "+old_pkgs(i);
write,f,format="Y_OTHERS =%s\n\n",pkg;
lib= fortran? " $(FORTRAN_LIBS)" : "";
write,f,format="SYS_LIBS =%s\n", lib;
if (fortran) write,f,format="FORTRAN_STYLE =%s\n", " $(WKS_FORTRAN)";
write,f,format="\nCLEAN_UP = %s\n\n",code_name;
write,f,format="%s\n",
"######################################################################";
write,f,format="# %s\n\n","Second section is targets for new package/code";
if (is_void(code_library)) lib= "$(NON_SHARABLE)";
else lib= "$(CODE_LIBRARY) $(CODE_NAME)";
write,f,format="all:: %s\n\n", lib;
write,f,format="%s\n%s\n%s\n%s\n%s\n\n",
"# Add header dependencies or special compile instructions here, e.g.-",
"#my_code1.o: my_code1.h my_code.h",
"#my_code2.o: my_code2.c my_code.h",
"#\t$(CC) $(CFLAGS) -DSPECIAL_SWITCH -c my_code2.c",
"#my_code3.o: my_code3.h my_code1.h my_code.h";
write,f,format="%s\n",
"######################################################################";
write,f,format="\n%s\n","include $(MAKE_TEMPLATE)";
close,f;
return;
oops:
write, "new packages must be in current directory";
write, "old packages must be in "+Y_SITE+"{i0,contrib}";
error, "unable to find package: "+f;
}
func undup_names (names)
{
/* remove duplicates from a list of names (or numbers) */
if (numberof(names)<2) return names;
require, "msort.i";
order= msort(names);
sorted= names(order);
list= where(sorted(1:-1)==sorted(2:0));
if (numberof(list)) {
names(order(list))= structof(names)(0);
names= names(where(names));
}
return names;
}
func get_keys (name, f, &srcs, &lib, &deplibs)
{
instrucs= get_instructions(f);
if (is_void(instrucs))
error, "no MAKE-INSTRUCTIONS comment found in "+name;
srcs= get_list(instrucs, "SRCS");
if (is_void(srcs))
error, "no SRCS in MAKE-INSTRUCTIONS comment found in "+name;
lib= get_list(instrucs, "LIB");
deplibs= get_list(instrucs, "DEPLIBS");
return instrucs;
}
func get_list (instrucs, keyword)
{
/* get keyword= from instruction lines returned by get_instructions
* return value is array of token strings
* -- allow for \ continuation of long lines */
list= where(instrucs(,1)==keyword);
if (!numberof(list)) return [];
i= list(1);
instrucs= instrucs(i:0,2);
list= array(pointer, numberof(instrucs));
len= array(0, numberof(list));
line= tokenize_line(instrucs(1), " \t=");
if (numberof(line)<2) return [];
line= line(2:0);
for (i=1 ; numberof(line)>=1 ; ++i) {
cont= (line(0)=="\\");
len(i)= n= numberof(line)-cont;
if (n) list(i)= &line(1:n);
if (!cont || i+1>=numberof(instrucs)) break;
line= tokenize_line(instrucs(i+1), " \t=");
}
n= sum(len);
if (!n) return [];
line= array(string, n);
for (i=1,j=0 ; j<n ; j+=len(i++)) {
if (!len(i)) continue;
line(j+1:j+len(i))= *list(i);
}
return line;
}
func tokenize_line (line, delim)
{
len= strlen(line);
if (!len) return [];
list= array(string, len);
len= 0;
do {
token= strtok(line, delim);
line= token(2);
token= token(1);
if (!token) break;
list(++len)= token;
} while (line);
if (!len) return [];
return list(1:len);
}
func get_instructions (f)
{
/* locate MAKE-INSTRUCTIONS comment and return 2xnlines
* array of [[first token, full line]] */
if (is_void(f)) return [];
do {
lines= rdline(f, 30);
token= strtok(lines);
list= where(token(1,)=="/*");
if (numberof(list)) {
starts= token(2,list);
list= list(where(strtok(starts)(1,)=="MAKE-INSTRUCTIONS"));
if (numberof(list)) {
list= list(1);
if (list>20) {
if (lines(0)) grow, lines, rdline(f, 10);
else grow, lines, array(string, 10);
}
lines= lines(list+1:list+10);
token= strtok(lines," \t=")(1,);
list= where(token=="*/");
if (!numberof(list) || list(1)<2) return [];
list= list(1)-1;
return [token(1:list), lines(1:list)];
}
}
} while (lines(0));
return [];
}
func find_file (name, path)
{
/* open file, which may be anywhere on optional supplied path */
if (is_void(path) ||
anyof(strpart(name,1:1)==["/","~","$"])) return open(name, "r", 1);
list= where(strpart(path,0:0)!="/");
if (numberof(list)) {
if (numberof(path)>1) path(list)+= "/";
else path+= "/";
}
for (i=1 ; i<numberof(path) ; ++i) {
f= open(path(i)+name, "r", 1);
if (!is_void(f)) return f;
}
return [];
}
if (batch()) {
command_line= get_argv();
if (numberof(command_line)>1) command_line= command_line(2:0);
else command_line= [];
make, command_line;
quit;
}
|