github.com/singularityware/singularity@v3.1.1+incompatible/makeit/genmod.awk (about)

     1  #!/usr/bin/awk -f
     2  # Copyright (c) 2015-2018, Yannick Cote <yhcote@gmail.com>. All rights reserved.
     3  # Use of this source code is governed by a BSD-style license that can be found
     4  # in the LICENSE file.
     5  
     6  function usage()
     7  {
     8  	print "usage: genmod mconflist=<module file> makeitgendir=<temp confdir>"
     9  	print "       host=<host type> tmpldir=<template location>"
    10  	exit(1)
    11  }
    12  
    13  # remove extra spaces from a string
    14  function trim(str)
    15  {
    16  	gsub(/  +/, " ", str)
    17  	gsub(/ +$/, "", str)
    18  	gsub(/^ +/, "", str)
    19  
    20  	return str
    21  }
    22  
    23  # print all keyword vars and their values for all project modules
    24  function printmvars()
    25  {
    26  	reset_file("/tmp/mvars")
    27  	for (m in mconfs) {
    28  		for (k in keywords) {
    29  			if (mvars[mconfs[m], keywords[k]] == "")
    30  				continue
    31  			printf("%s:%s [%s]\n", mconfs[m], keywords[k],
    32  			       mvars[mconfs[m], keywords[k]]) >> "/tmp/mvars"
    33  		}
    34  		print "" >> "/tmp/mvars"
    35  	}
    36  }
    37  
    38  # truncate file
    39  function reset_file(file)
    40  {
    41  	printf("") > file
    42  }
    43  
    44  # check if we are still reading keyword values or reached a new keyword
    45  function getkeyword()
    46  {
    47  	iskey = 0
    48  
    49  	if (words[1] != "") {
    50  		for (k in keywords) {
    51  			if (words[1] == keywords[k])
    52  				iskey = 1
    53  		}
    54  		if (iskey == 1) {
    55  			currkeywd = words[1]
    56  		} else {
    57  			print "error:", words[1], "is not a keyword"
    58  			exit(1)
    59  		}
    60  	}
    61  }
    62  
    63  # for a keyword (name, src, cflags, etc.) read its values
    64  function getvalues(mconf, nfields)
    65  {
    66  	for (j = 2; j <= nfields; j++) {
    67  		mvars[mconf, currkeywd] = mvars[mconf, currkeywd] " " words[j]
    68  	}
    69  	mvars[mconf, currkeywd] = trim(mvars[mconf, currkeywd])
    70  }
    71  
    72  # this routine reads and parses all mconf variables from one "mconf" file
    73  function scanmconf(mconf, makeitgendir)
    74  {
    75  	m = makeitgendir "/" mconf ".parsed"
    76  	while (getline < m > 0) {
    77  		n = split($0, words, " *:= *| *\\ *|[ \t]*")
    78  		if (n > 0) {
    79  			getkeyword()
    80  			getvalues(mconf, n)
    81  		}
    82  	}
    83  }
    84  
    85  # generate object list from [a,c]src,win_[a,c]src,unix_[a,c]src for each module
    86  function genobjs(mconf)
    87  {
    88  	# first "[a,c]obj"
    89  	split(mvars[mconf, "csrc"], objs, " ")
    90  	for (o in objs) {
    91  		gsub(/\.c$/, ".o", objs[o])
    92  		mvars[mconf, "cobj"] = mvars[mconf, "cobj"] "$(BUILDDIR)/" objs[o] " "
    93  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
    94  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o] ".d"
    95  	}
    96  
    97  	split(mvars[mconf, "asrc"], objs, " ")
    98  	for (o in objs) {
    99  		gsub(/\.S$/, ".o", objs[o])
   100  		mvars[mconf, "aobj"] = mvars[mconf, "aobj"] "$(BUILDDIR)/" objs[o] " "
   101  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
   102  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o] ".d"
   103  	}
   104  
   105  	# then "unix_[a,c]obj"
   106  	split(mvars[mconf, "unix_csrc"], objs, " ")
   107  	for (o in objs) {
   108  		gsub(/\.c$/, ".o", objs[o])
   109  		mvars[mconf, "unix_cobj"] = mvars[mconf, "unix_cobj"] "$(BUILDDIR)/" objs[o] " "
   110  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
   111  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o] ".d"
   112  	}
   113  
   114  	split(mvars[mconf, "unix_asrc"], objs, " ")
   115  	for (o in objs) {
   116  		gsub(/\.S$/, ".o", objs[o])
   117  		mvars[mconf, "unix_aobj"] = mvars[mconf, "unix_aobj"] "$(BUILDDIR)/" objs[o] " "
   118  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
   119  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o] ".d"
   120  	}
   121  
   122  	# then "win_[a,c]obj"
   123  	split(mvars[mconf, "win_csrc"], objs, " ")
   124  	for (o in objs) {
   125  		gsub(/\.c$/, ".o", objs[o])
   126  		mvars[mconf, "win_cobj"] = mvars[mconf, "win_cobj"] "$(BUILDDIR)/" objs[o] " "
   127  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
   128  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o] ".d"
   129  	}
   130  
   131  	split(mvars[mconf, "win_asrc"], objs, " ")
   132  	for (o in objs) {
   133  		gsub(/\.S$/, ".o", objs[o])
   134  		mvars[mconf, "win_aobj"] = mvars[mconf, "win_aobj"] "$(BUILDDIR)/" objs[o] " "
   135  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
   136  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o] ".d"
   137  	}
   138  
   139  	# finally "data"
   140  	split(mvars[mconf, "data"], objs, " ")
   141  	for (o in objs) {
   142  		objs[o] = objs[o] ".bin"
   143  		mvars[mconf, "dobj"] = mvars[mconf, "dobj"] objs[o] " "
   144  		mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(BUILDDIR)/" objs[o]
   145  	}
   146  }
   147  
   148  # create the "target" mconf variable based on kind (prog, lib, obj list)
   149  function gentarget(mconf)
   150  {
   151  	if (mvars[mconf, "prog"] != "") {
   152  		# generate target for a program
   153  		mvars[mconf, "target"] = mvars[mconf, "prog"]
   154  		if (envar["host"] == "windows")
   155  			mvars[mconf, "target"] = mvars[mconf, "target"] ".exe"
   156  	} else if (mvars[mconf, "lib"] != "") {
   157  		# generate target for a library
   158  		mvars[mconf, "target"] = "lib" mvars[mconf, "lib"]
   159  	} else if (mvars[mconf, "data"] != "") {
   160  		# generate target for embedded data
   161  		mvars[mconf, "target"] = mvars[mconf, "name"] ".bin.o"
   162  	} else {
   163  		# generate target for a simple list of objects
   164  		mvars[mconf, "target"] = mvars[mconf, "name"] "_OBJ"
   165  	}
   166  }
   167  
   168  # create a list of "deps_link" list based on other mconf target this mconf needs to link with
   169  function gendeps_link(mconf, idx)
   170  {
   171  	# dependency is a program nothing to link with
   172  	if (mvars[mconfs[mconf], "prog"] != "")
   173  		return
   174  
   175  	# dependency is a lib, generate library link rules
   176  	if (mvars[mconfs[mconf], "lib"] != "") {
   177  		mvars[mconfs[idx], "deps_link"] = mvars[mconfs[idx], "deps_link"] " " \
   178  			"-L$(BUILDDIR)/" mconfsdirs[mconf] " -l" mvars[mconfs[mconf], "lib"]
   179  	} else if (mvars[mconfs[mconf], "data"] != "") {
   180  		mvars[mconfs[idx], "extralibs"] = "$(" mvars[mconfs[mconf], "target"] ")" " " \
   181  			mvars[mconfs[idx], "extralibs"]
   182  	} else {
   183  		# dependency is just an object list
   184  		mvars[mconfs[idx], "deps_link"] = "$(" mvars[mconfs[mconf], "target"] ")" " " \
   185  			mvars[mconfs[idx], "deps_link"]
   186  	}
   187  }
   188  
   189  # create a "deps_target" list based on other mconf targets this mconf depends on
   190  function gendeps(idx)
   191  {
   192  	split(mvars[mconfs[idx], "depends"], deps, " ")
   193  	for (d in deps) {
   194  		found = 0
   195  		for (m in mconfs) {
   196  			if (mvars[mconfs[m], "name"] == deps[d]) {
   197  				gendeps_link(m, idx)
   198  				mvars[mconfs[idx], "deps_target"] = mvars[mconfs[idx], "deps_target"]  " " \
   199  				     "$(" mvars[mconfs[m], "target"] ")"
   200  				found = 1
   201  			}
   202  		}
   203  		# if dependency is NOT a module name but just a verbatim expression to paste in place
   204  		if (found == 0) {
   205  			mvars[mconfs[idx], "deps_target"] = mvars[mconfs[idx], "deps_target"] " " deps[d]
   206  		}
   207  	}
   208  	mvars[mconfs[idx], "deps_link"] = trim(mvars[mconfs[idx], "deps_link"])
   209  	mvars[mconfs[idx], "deps_target"] = trim(mvars[mconfs[idx], "deps_target"])
   210  }
   211  
   212  # output all object lists rules for a specific .mconf file
   213  function put_objlist(mconf, cobj, aobj, output)
   214  {
   215  	printf("# object files list\n") >> output
   216  	printf("%s_OBJ := \\\n", mvars[mconf, "name"]) >> output
   217  	
   218  	split(cobj, objs, " ")
   219  	for (o in objs) {
   220  		printf("\t%s \\\n", objs[o]) >> output
   221  	}
   222  	split(aobj, objs, " ")
   223  	for (o in objs) {
   224  		printf("\t%s \\\n", objs[o]) >> output
   225  	}
   226  	split(mvars[mconf, "dobj"], objs, " ")
   227  	for (o in objs) {
   228  		printf("\t%s \\\n", objs[o]) >> output
   229  	}
   230  	print "" >> output
   231  }
   232  
   233  # output all suffix (build) rules for a specific .mconf file
   234  function put_suffix_rules(cobj, aobj, mconf, output)
   235  {
   236  	printf("# suffix rules (metarules missing from most variants)\n") >> output
   237  
   238  	split(cobj, objs, " ")
   239  	for (o in objs) {
   240  		# prepare the source file name `s' out of `o'
   241  		s = objs[o]
   242  		gsub(/\.o$/, ".c", s)
   243  		gsub(/^\$\(BUILDDIR\)\//, "", s)
   244  
   245  		# fix up the target template when building generated source files
   246  		if (match(s, /^\$\(BUILDDIR\)\//) == 1) {
   247  			tmpl = envar["tmpldir"] "/" "suffix_bldir.tmpl"
   248  			n = split(s, gen, "/")
   249  			while (getline < tmpl > 0) {
   250  				gsub(/__OBJ__/, objs[o], $0)
   251  				gsub(/__SRC__/, s, $0)
   252  				gsub(/__GENSRC__/, "[GEN] " gen[n], $0)
   253  				gsub(/__CFLAGS__/, mvars[mconf, "cflags"], $0)
   254  				# write the result down in the current fragment
   255  				if ($0 != "") {
   256  					$0 = trim($0)
   257  					printf("%s\n", $0) >> output
   258  				}
   259  			}
   260  			close(tmpl)
   261  		} else {
   262  			tmpl = envar["tmpldir"] "/" "suffix.tmpl"
   263  			while (getline < tmpl > 0) {
   264  				gsub(/__OBJ__/, objs[o], $0)
   265  				gsub(/__SRC__/, s, $0)
   266  				gsub(/__CFLAGS__/, mvars[mconf, "cflags"], $0)
   267  				# write the result down in the current fragment
   268  				if ($0 != "") {
   269  					$0 = trim($0)
   270  					printf("%s\n", $0) >> output
   271  				}
   272  			}
   273  			close(tmpl)
   274  		}
   275  	}
   276  	split(aobj, objs, " ")
   277  	for (o in objs) {
   278  		# prepare the source file name `s' out of `o'
   279  		s = objs[o]
   280  		gsub(/\.o$/, ".S", s)
   281  		gsub(/^\$\(BUILDDIR\)\//, "", s)
   282  
   283  		# fix up the target template when building generated source files
   284  		if (match(s, /^\$\(BUILDDIR\)\//) == 1) {
   285  			tmpl = envar["tmpldir"] "/" "suffix_bldir.tmpl"
   286  			n = split(s, gen, "/")
   287  			while (getline < tmpl > 0) {
   288  				gsub(/__OBJ__/, objs[o], $0)
   289  				gsub(/__SRC__/, s, $0)
   290  				gsub(/__GENSRC__/, "[GEN] " gen[n], $0)
   291  				gsub(/__CFLAGS__/, mvars[mconf, "cflags"], $0)
   292  				# write the result down in the current fragment
   293  				if ($0 != "") {
   294  					$0 = trim($0)
   295  					printf("%s\n", $0) >> output
   296  				}
   297  			}
   298  			close(tmpl)
   299  		} else {
   300  			tmpl = envar["tmpldir"] "/" "suffix.tmpl"
   301  			while (getline < tmpl > 0) {
   302  				gsub(/__OBJ__/, objs[o], $0)
   303  				gsub(/__SRC__/, s, $0)
   304  				gsub(/__CFLAGS__/, mvars[mconf, "cflags"], $0)
   305  				# write the result down in the current fragment
   306  				if ($0 != "") {
   307  					$0 = trim($0)
   308  					printf("%s\n", $0) >> output
   309  				}
   310  			}
   311  			close(tmpl)
   312  		}
   313  	}
   314  	split(mvars[mconf, "dobj"], objs, " ")
   315  	for (o in objs) {
   316  		# prepare the source file name `s' out of `o'
   317  		s = objs[o]
   318  		gsub(/\.bin$/, "", s)
   319  		gsub(/^\$\(BUILDDIR\)\//, "", s)
   320  
   321  		tmpl = envar["tmpldir"] "/" "suffix_data.tmpl"
   322  		n = split(s, gen, "/")
   323  		while (getline < tmpl > 0) {
   324  			gsub(/__OBJ__/, objs[o], $0)
   325  			gsub(/__SRC__/, s, $0)
   326  			# write the result down in the current fragment
   327  			if ($0 != "") {
   328  				$0 = trim($0)
   329  				printf("%s\n", $0) >> output
   330  			}
   331  		}
   332  		close(tmpl)
   333  	}
   334  
   335  	print "" >> output
   336  }
   337  
   338  # output a "prog" target
   339  function put_prog(mconf, mconfdir, output)
   340  {
   341  	tmpl = envar["tmpldir"] "/" "prog.tmpl"
   342  	prefix = ""
   343  
   344  	printf("# link the program `%s'\n", mvars[mconf, "target"]) >> output
   345  	while (getline < tmpl > 0) {
   346  		gsub(/__TARGET__/, mvars[mconf, "target"], $0)
   347  		gsub(/__PATH__/, mconfdir, $0)
   348  		gsub(/__NAME__/, mvars[mconf, "name"], $0)
   349  		gsub(/__DEPEND_T__/, mvars[mconf, "deps_target"], $0)
   350  		gsub(/__DEPEND_L__/, mvars[mconf, "deps_link"], $0)
   351  		gsub(/__LDFLAGS__/, mvars[mconf, "ldflags"], $0)
   352  		gsub(/__EXTRALIBS__/, mvars[mconf, "extralibs"], $0)
   353  		$0 = trim($0)
   354  		printf("%s\n", $0) >> output
   355  	}
   356  	close(tmpl)
   357  	print "" >> output
   358  
   359  	mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(" mvars[mconf, "target"] ")"
   360  }
   361  
   362  # output a "lib" target
   363  function put_lib(mconf, mconfdir, output)
   364  {
   365  	tmpl = envar["tmpldir"] "/" "lib.tmpl"
   366  
   367  	printf("# create lib `%s'\n", mvars[mconf, "target"]) >> output
   368  	while (getline < tmpl > 0) {
   369  		gsub(/__TARGET__/, mvars[mconf, "target"], $0)
   370  		gsub(/__PATH__/, mconfdir, $0)
   371  		gsub(/__NAME__/, mvars[mconf, "name"], $0)
   372  		$0 = trim($0)
   373  		printf("%s\n", $0) >> output
   374  	}
   375  	close(tmpl)
   376  	print "" >> output
   377  
   378  	mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(" mvars[mconf, "target"] ")"
   379  }
   380  
   381  # output a "data" target
   382  function put_data(mconf, mconfdir, output)
   383  {
   384  	tmpl = envar["tmpldir"] "/" "data.tmpl"
   385  
   386  	printf("# create embedded data object `%s'\n", mvars[mconf, "target"]) >> output
   387  	while (getline < tmpl > 0) {
   388  		gsub(/__TARGET__/, mvars[mconf, "target"], $0)
   389  		gsub(/__PATH__/, mconfdir, $0)
   390  		gsub(/__NAME__/, mvars[mconf, "name"], $0)
   391  		$0 = trim($0)
   392  		printf("%s\n", $0) >> output
   393  	}
   394  	close(tmpl)
   395  	print "" >> output
   396  
   397  	mvars[mconf, "cleanfiles"] = mvars[mconf, "cleanfiles"] " " "$(" mvars[mconf, "target"] ")"
   398  }
   399  
   400  # generate 1 .mk file for specified .mconf -- to be inlined in top Makefile
   401  function put_mkfile(mconf, mconfdir, output)
   402  {
   403  	# gather objects from C files
   404  	cob = mvars[mconf, "cobj"]
   405  	if (envar["host"] == "unix")
   406  		cob = cob " " mvars[mconf, "unix_cobj"]
   407  	if (envar["host"] == "windows")
   408  		cob = cob " " mvars[mconf, "win_cobj"]
   409  
   410  	# gather objects from assembly .S files
   411  	aob = mvars[mconf, "aobj"]
   412  	if (envar["host"] == "unix")
   413  		aob = aob " " mvars[mconf, "unix_aobj"]
   414  	if (envar["host"] == "windows")
   415  		aob = aob " " mvars[mconf, "win_aobj"]
   416  
   417  	# write list of objects to build
   418  	put_objlist(mconf, cob, aob, output)
   419  
   420  	# if the mconf module is a program, write link rules
   421  	if (mvars[mconf, "prog"] != "")
   422  		put_prog(mconf, mconfdir, output)
   423  
   424  	# if the mconf module is a library, write lib creation rules
   425  	if (mvars[mconf, "lib"] != "")
   426  		put_lib(mconf, mconfdir, output)
   427  
   428  	# if the mconf module is embedded data objects, write data object creation rules
   429  	if (mvars[mconf, "data"] != "")
   430  		put_data(mconf, mconfdir, output)
   431  
   432  	# write each object suffix build rules
   433  	put_suffix_rules(cob, aob, mconf, output)
   434  }
   435  
   436  # generate the all: rule starting with loose targets, then libs, then programs in that order
   437  # then generate the CLEANFILES rule
   438  function genallrule(output)
   439  {
   440  	combined_mconfsfile = envar["makeitgendir"] "/combined-mconfsready.mk"
   441  	reset_file(combined_mconfsfile)
   442  
   443  	printf("all:") > output
   444  	# write targets that are NOT libraries of programs first
   445  	for (m in mconfs) {
   446  		if (mvars[mconfs[m], "prog"] == "" && mvars[mconfs[m], "lib"] == "") {
   447  			printf(" $(%s)", mvars[mconfs[m], "target"]) >> output
   448  			put_mkfile(mconfs[m], mconfsdirs[m], combined_mconfsfile)
   449  		}
   450  	}
   451  	# then libraries
   452  	for (m in mconfs) {
   453  		if (mvars[mconfs[m], "lib"] != "") {
   454  			printf(" $(%s)", mvars[mconfs[m], "target"]) >> output
   455  			put_mkfile(mconfs[m], mconfsdirs[m], combined_mconfsfile)
   456  		}
   457  	}
   458  	# finally programs
   459  	for (m in mconfs) {
   460  		if (mvars[mconfs[m], "prog"] != "") {
   461  			printf(" $(%s)", mvars[mconfs[m], "target"]) >> output
   462  			put_mkfile(mconfs[m], mconfsdirs[m], combined_mconfsfile)
   463  		}
   464  	}
   465  
   466  	# collect cleanfiles from all mconfs and set a CLEANFILES make var
   467  	for (m in mconfs) {
   468  		cl = cl " " mvars[mconfs[m], "cleanfiles"] " "
   469  	}
   470  	cl = trim(cl)
   471  	printf("\n\nCLEANFILES += %s\n", cl) >> output
   472  }
   473  
   474  # check the parameters passed to the program
   475  function checkvars()
   476  {
   477  	if (envar["mconflist"] == "")
   478  		usage()
   479  	if (envar["host"] == "")
   480  		usage()
   481  	if (envar["makeitgendir"] == "")
   482  		usage()
   483  	if (envar["tmpldir"] == "")
   484  		usage()
   485  }
   486  
   487  # main entry
   488  BEGIN {
   489  	# variable defs
   490  	mconfs[0] = ""
   491  	mconfsdirs[0] = ""
   492  	nmconfs = 0
   493  	mvars[0] = ""
   494  	words[0] = ""
   495  	currkeywd = ""
   496  	envar[0] = ""
   497  	keywords[0] = ""
   498  	klist = "name prog lib asrc data csrc win_asrc win_csrc unix_asrc unix_csrc"
   499  	klist = klist " " "depends cflags ldflags extralibs cleanfiles"
   500  
   501  	# init keywords
   502  	split(klist, keywords, " ")
   503  
   504  	# collect program environment vars from command line ARGV array
   505  	for (i = 0; i < ARGC; i++) {
   506  		n = split(ARGV[i], args, "=")
   507  		if (n == 2)
   508  			envar[args[1]] = args[2]
   509  	}
   510  
   511  	# check that we were called with all needed environment vars
   512  	checkvars()
   513  
   514  	# extract mconf dirname and basename from mconfig generated module.lst
   515  	while (getline < envar["mconflist"] > 0) {
   516  		mconfs[nmconfs] = $1 "/" $2
   517  		mconfsdirs[nmconfs] = $1
   518  		nmconfs++
   519  	}
   520  
   521  	# for all .mconf files found, 1) parse, 2) gen src/obj lists, 3) gen make targets
   522  	for (i = 0; i < nmconfs; i++) {
   523  		scanmconf(mconfs[i], envar["makeitgendir"])
   524  		genobjs(mconfs[i])
   525  		gentarget(mconfs[i])
   526  	}
   527  
   528  	# for each make targets generated above, generate dependency rules
   529  	for (i = 0; i < nmconfs; i++) {
   530  		gendeps(i)
   531  	}
   532  
   533  	# finally, generate the "all:" rule listing all target in dependency order
   534  	genallrule(envar["makeitgendir"] "/" "all.mk")
   535  
   536  #	printmvars()
   537  }