1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License').  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * Mach Operating System
26 * Copyright (c) 1990 Carnegie-Mellon University
27 * Copyright (c) 1989 Carnegie-Mellon University
28 * Copyright (c) 1988 Carnegie-Mellon University
29 * Copyright (c) 1987 Carnegie-Mellon University
30 * All rights reserved.  The CMU software License Agreement specifies
31 * the terms and conditions for use and redistribution.
32 */
33
34/*
35 * Copyright (c) 1980 Regents of the University of California.
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms are permitted
39 * provided that the above copyright notice and this paragraph are
40 * duplicated in all such forms and that any documentation,
41 * advertising materials, and other materials related to such
42 * distribution and use acknowledge that the software was developed
43 * by the University of California, Berkeley.  The name of the
44 * University may not be used to endorse or promote products derived
45 * from this software without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
47 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
48 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
49 */
50
51#ifndef lint
52static char sccsid[] __attribute__((used)) = "@(#)mkmakefile.c	5.21 (Berkeley) 6/18/88";
53#endif /* not lint */
54
55/*
56 * Build the makefile for the system, from
57 * the information in the files files and the
58 * additional files for the machine being compiled to.
59 */
60
61#include <stdio.h>
62#include <unistd.h>	/* for unlink */
63#include <ctype.h>
64#include "parser.h"
65#include "config.h"
66
67void	read_files(void);
68void	do_objs(FILE *fp, const char *msg, int ext);
69void	do_files(FILE *fp, const char *msg, char ext);
70void	do_machdep(FILE *ofp);
71void	do_rules(FILE *f);
72void	copy_dependencies(FILE *makin, FILE *makout);
73
74struct file_list *fl_lookup(char *file);
75struct file_list *fltail_lookup(char *file);
76struct file_list *new_fent(void);
77
78void	put_source_file_name(FILE *fp, struct file_list *tp);
79
80
81#define next_word(fp, wd) \
82	{ register const char *word = get_word(fp); \
83	  if (word == (char *)EOF) \
84		return; \
85	  else \
86		wd = word; \
87	}
88
89static	struct file_list *fcur;
90const char *tail(const char *fn);
91char *allCaps(char *str);
92
93/*
94 * Lookup a file, by name.
95 */
96struct file_list *
97fl_lookup(char *file)
98{
99	register struct file_list *fp;
100
101	for (fp = ftab ; fp != 0; fp = fp->f_next) {
102		if (eq(fp->f_fn, file))
103			return (fp);
104	}
105	return (0);
106}
107
108/*
109 * Lookup a file, by final component name.
110 */
111struct file_list *
112fltail_lookup(char *file)
113{
114	register struct file_list *fp;
115
116	for (fp = ftab ; fp != 0; fp = fp->f_next) {
117		if (eq(tail(fp->f_fn), tail(file)))
118			return (fp);
119	}
120	return (0);
121}
122
123/*
124 * Make a new file list entry
125 */
126struct file_list *
127new_fent(void)
128{
129	register struct file_list *fp;
130
131	fp = (struct file_list *) malloc(sizeof *fp);
132	fp->f_needs = 0;
133	fp->f_next = 0;
134	fp->f_flags = 0;
135	fp->f_type = 0;
136	fp->f_extra = (char *) 0;
137	if (fcur == 0)
138		fcur = ftab = fp;
139	else
140		fcur->f_next = fp;
141	fcur = fp;
142	return (fp);
143}
144
145char	*COPTS;
146
147const char *
148get_VPATH(void)
149{
150    static char *vpath = NULL;
151
152    if ((vpath == NULL) &&
153	((vpath = getenv("VPATH")) != NULL) &&
154	(*vpath != ':')) {
155	register char *buf = malloc((unsigned)(strlen(vpath) + 2));
156
157	vpath = strcat(strcpy(buf, ":"), vpath);
158    }
159
160    return vpath ? vpath : "";
161}
162
163
164/*
165 * Build the makefile from the skeleton
166 */
167void
168makefile(void)
169{
170	FILE *ifp, *ofp;
171	FILE *dfp;
172	char pname[BUFSIZ];
173	char line[BUFSIZ];
174	struct opt *op;
175
176	read_files();
177	(void) sprintf(line, "%s/Makefile.template", config_directory);
178	ifp = fopenp(VPATH, line, pname, "r");
179	if (ifp == 0) {
180		perror(line);
181		exit(1);
182	}
183	dfp = fopen(path("Makefile"), "r");
184	rename(path("Makefile"), path("Makefile.old"));
185	unlink(path("Makefile.old"));
186	ofp = fopen(path("Makefile"), "w");
187	if (ofp == 0) {
188		perror(path("Makefile"));
189		exit(1);
190	}
191	fprintf(ofp, "SOURCE_DIR=%s\n", source_directory);
192
193	fprintf(ofp, "export CONFIG_DEFINES =");
194	if (profiling)
195		fprintf(ofp, " -DGPROF");
196
197	for (op = opt; op; op = op->op_next)
198		if (op->op_value)
199			fprintf(ofp, " -D%s=\"%s\"", op->op_name, op->op_value);
200		else
201			fprintf(ofp, " -D%s", op->op_name);
202	fprintf(ofp, "\n");
203	for (op = mkopt; op; op = op->op_next)
204		if (op->op_value)
205			fprintf(ofp, "%s=%s\n", op->op_name, op->op_value);
206		else
207			fprintf(ofp, "%s\n", op->op_name);
208
209	while (fgets(line, BUFSIZ, ifp) != 0) {
210		if (*line == '%')
211			goto percent;
212		if (profiling && strncmp(line, "COPTS=", 6) == 0) {
213			register char *cp;
214			fprintf(ofp,
215				"GPROF.EX=$(SOURCE_DIR)/machdep/%s/gmon.ex\n", machinename);
216			cp = index(line, '\n');
217			if (cp)
218				*cp = 0;
219			cp = line + 6;
220			while (*cp && (*cp == ' ' || *cp == '\t'))
221				cp++;
222			COPTS = malloc((unsigned)(strlen(cp) + 1));
223			if (COPTS == 0) {
224				printf("config: out of memory\n");
225				exit(1);
226			}
227			strcpy(COPTS, cp);
228			fprintf(ofp, "%s -pg\n", line);
229			continue;
230		}
231		fprintf(ofp, "%s", line);
232		continue;
233	percent:
234		if (eq(line, "%OBJS\n")) {
235			do_objs(ofp, "OBJS=", -1);
236		} else if (eq(line, "%CFILES\n")) {
237			do_files(ofp, "CFILES=", 'c');
238			do_objs(ofp, "COBJS=", 'c');
239		} else if (eq(line, "%CXXFILES\n")) {
240			do_files(ofp, "CXXFILES=", 'p');
241			do_objs(ofp, "CXXOBJS=", 'p');
242		} else if (eq(line, "%SFILES\n")) {
243			do_files(ofp, "SFILES=", 's');
244			do_objs(ofp, "SOBJS=", 's');
245		} else if (eq(line, "%MACHDEP\n")) {
246			do_machdep(ofp);
247		} else if (eq(line, "%RULES\n"))
248			do_rules(ofp);
249		else
250			fprintf(stderr,
251			    "Unknown %% construct in generic makefile: %s",
252			    line);
253	}
254	if (dfp != NULL)
255	{
256		copy_dependencies(dfp, ofp);
257		(void) fclose(dfp);
258	}
259	(void) fclose(ifp);
260	(void) fclose(ofp);
261}
262
263/*
264 * Read in the information about files used in making the system.
265 * Store it in the ftab linked list.
266 */
267void
268read_files(void)
269{
270	FILE *fp;
271	register struct file_list *tp, *pf;
272	register struct device *dp;
273	register struct opt *op;
274	const char *wd;
275	char *this, *needs;
276	const char *devorprof;
277	int options;
278	int not_option;
279	char pname[BUFSIZ];
280	char fname[1024];
281	char *rest = (char *) 0;
282	int nreqs, first = 1, isdup;
283
284	ftab = 0;
285	(void) sprintf(fname, "%s/files", config_directory);
286openit:
287	fp = fopenp(VPATH, fname, pname, "r");
288	if (fp == 0) {
289		perror(fname);
290		exit(1);
291	}
292next:
293	options = 0;
294	rest = (char *) 0;
295	/*
296	 * filename	[ standard | optional ]
297	 *	[ dev* | profiling-routine ] [ device-driver]
298	 */
299	wd = get_word(fp);
300	if (wd == (char *)EOF) {
301		(void) fclose(fp);
302		if (first == 1) {
303			(void) sprintf(fname, "%s/files.%s", config_directory, machinename);
304			first++;
305			goto openit;
306		}
307		return;
308	}
309	if (wd == 0)
310		goto next;
311	/*
312	 *  Allow comment lines beginning witha '#' character.
313	 */
314	if (*wd == '#')
315	{
316		while ((wd=get_word(fp)) && wd != (char *)EOF)
317			;
318		goto next;
319	}
320
321	this = ns(wd);
322	next_word(fp, wd);
323	if (wd == 0) {
324		printf("%s: No type for %s.\n",
325		    fname, this);
326		exit(1);
327	}
328	if ((pf = fl_lookup(this)) && (pf->f_type != INVISIBLE || pf->f_flags))
329		isdup = 1;
330	else
331		isdup = 0;
332	tp = 0;
333	nreqs = 0;
334	devorprof = "";
335	needs = 0;
336	if (eq(wd, "standard"))
337		goto checkdev;
338	if (!eq(wd, "optional")) {
339		printf("%s: %s must be optional or standard\n", fname, this);
340		exit(1);
341	}
342	if (strncmp(this, "OPTIONS/", 8) == 0)
343		options++;
344	not_option = 0;
345nextopt:
346	next_word(fp, wd);
347	if (wd == 0)
348		goto doneopt;
349	if (eq(wd, "not")) {
350		not_option = !not_option;
351		goto nextopt;
352	}
353	devorprof = wd;
354	if (eq(wd, "device-driver") || eq(wd, "profiling-routine")) {
355		next_word(fp, wd);
356		goto save;
357	}
358	nreqs++;
359	if (needs == 0 && nreqs == 1)
360		needs = ns(wd);
361	if (isdup)
362		goto invis;
363	if (options)
364	{
365		struct opt *lop = 0;
366		struct device tdev;
367
368		/*
369		 *  Allocate a pseudo-device entry which we will insert into
370		 *  the device list below.  The flags field is set non-zero to
371		 *  indicate an internal entry rather than one generated from
372		 *  the configuration file.  The slave field is set to define
373		 *  the corresponding symbol as 0 should we fail to find the
374		 *  option in the option list.
375		 */
376		init_dev(&tdev);
377		tdev.d_name = ns(wd);
378		tdev.d_type = PSEUDO_DEVICE;
379		tdev.d_flags++;
380		tdev.d_slave = 0;
381
382		for (op=opt; op; lop=op, op=op->op_next)
383		{
384			char *od = allCaps(ns(wd));
385
386			/*
387			 *  Found an option which matches the current device
388			 *  dependency identifier.  Set the slave field to
389			 *  define the option in the header file.
390			 */
391			if (strcmp(op->op_name, od) == 0)
392			{
393				tdev.d_slave = 1;
394				if (lop == 0)
395					opt = op->op_next;
396				else
397					lop->op_next = op->op_next;
398				free(op);
399				op = 0;
400			 }
401			free(od);
402			if (op == 0)
403				break;
404		}
405		newdev(&tdev);
406	}
407 	for (dp = dtab; dp != 0; dp = dp->d_next) {
408		if (eq(dp->d_name, wd) && (dp->d_type != PSEUDO_DEVICE || dp->d_slave)) {
409			if (not_option)
410				goto invis;	/* dont want file if option present */
411			else
412				goto nextopt;
413		}
414	}
415	if (not_option)
416		goto nextopt;		/* want file if option missing */
417
418	for (op = opt; op != 0; op = op->op_next)
419		if (op->op_value == 0 && opteq(op->op_name, wd)) {
420			if (nreqs == 1) {
421				free(needs);
422				needs = 0;
423			}
424			goto nextopt;
425		}
426
427invis:
428	while ((wd = get_word(fp)) != 0)
429		;
430	if (tp == 0)
431		tp = new_fent();
432	tp->f_fn = this;
433	tp->f_type = INVISIBLE;
434	tp->f_needs = needs;
435	tp->f_flags = isdup;
436	goto next;
437
438doneopt:
439	if (nreqs == 0) {
440		printf("%s: what is %s optional on?\n",
441		    fname, this);
442		exit(1);
443	}
444
445checkdev:
446	if (wd) {
447		if (*wd == '|')
448			goto getrest;
449		next_word(fp, wd);
450		if (wd) {
451			devorprof = wd;
452			next_word(fp, wd);
453		}
454	}
455
456save:
457getrest:
458	if (wd) {
459		if (*wd == '|') {
460			rest = ns(get_rest(fp));
461		} else {
462			printf("%s: syntax error describing %s\n",
463			       fname, this);
464			exit(1);
465		}
466	}
467	if (eq(devorprof, "profiling-routine") && profiling == 0)
468		goto next;
469	if (tp == 0)
470		tp = new_fent();
471	tp->f_fn = this;
472	tp->f_extra = rest;
473	if (options)
474		tp->f_type = INVISIBLE;
475	else
476	if (eq(devorprof, "device-driver"))
477		tp->f_type = DRIVER;
478	else if (eq(devorprof, "profiling-routine"))
479		tp->f_type = PROFILING;
480	else
481		tp->f_type = NORMAL;
482	tp->f_flags = 0;
483	tp->f_needs = needs;
484	if (pf && pf->f_type == INVISIBLE)
485		pf->f_flags = 1;		/* mark as duplicate */
486	goto next;
487}
488
489int
490opteq(const char *cp, const char *dp)
491{
492	char c, d;
493
494	for (; ; cp++, dp++) {
495		if (*cp != *dp) {
496			c = isupper(*cp) ? tolower(*cp) : *cp;
497			d = isupper(*dp) ? tolower(*dp) : *dp;
498			if (c != d)
499				return (0);
500		}
501		if (*cp == 0)
502			return (1);
503	}
504}
505
506void
507put_source_file_name(FILE *fp, struct file_list *tp)
508{
509	if ((tp->f_fn[0] == '.') && (tp->f_fn[1] == '/'))
510		fprintf(fp, "%s ", tp->f_fn);
511	 else
512		fprintf(fp, "$(SOURCE_DIR)/%s ", tp->f_fn);
513}
514
515void
516do_objs(FILE *fp, const char *msg, int ext)
517{
518	register struct file_list *tp;
519	register int lpos, len;
520	char *cp;
521	char och;
522	const char *sp;
523
524	fprintf(fp, "%s", msg);
525	lpos = strlen(msg);
526	for (tp = ftab; tp != 0; tp = tp->f_next) {
527		if (tp->f_type == INVISIBLE)
528			continue;
529
530		/*
531		 *	Check for '.o' file in list
532		 */
533		cp = tp->f_fn + (len = strlen(tp->f_fn)) - 1;
534		if (ext != -1 && *cp != ext)
535			continue;
536		else if (*cp == 'o') {
537			if (len + lpos > 72) {
538				lpos = 8;
539				fprintf(fp, "\\\n\t");
540			}
541			put_source_file_name(fp, tp);
542			fprintf(fp, " ");
543			lpos += len + 1;
544			continue;
545		}
546		sp = tail(tp->f_fn);
547		cp = (char *)sp + (len = strlen(sp)) - 1;
548		och = *cp;
549		*cp = 'o';
550		if (len + lpos > 72) {
551			lpos = 8;
552			fprintf(fp, "\\\n\t");
553		}
554		fprintf(fp, "%s ", sp);
555		lpos += len + 1;
556		*cp = och;
557	}
558	putc('\n', fp);
559}
560
561void
562do_files(FILE *fp, const char *msg, char ext)
563{
564	register struct file_list *tp;
565	register int lpos, len=0; /* dvw: init to 0 */
566
567	fprintf(fp, "%s", msg);
568	lpos = 8;
569	for (tp = ftab; tp != 0; tp = tp->f_next) {
570		if (tp->f_type == INVISIBLE)
571			continue;
572		if (tp->f_fn[strlen(tp->f_fn)-1] != ext)
573			continue;
574		/*
575		 * Always generate a newline.
576		 * Our Makefile's aren't readable anyway.
577		 */
578
579		lpos = 8;
580		fprintf(fp, "\\\n\t");
581		put_source_file_name(fp, tp);
582		lpos += len + 1;
583	}
584	putc('\n', fp);
585}
586
587/*
588 *  Include machine dependent makefile in output
589 */
590
591void
592do_machdep(FILE *ofp)
593{
594	FILE *ifp;
595	char pname[BUFSIZ];
596	char line[BUFSIZ];
597
598	(void) sprintf(line, "%s/Makefile.%s", config_directory, machinename);
599	ifp = fopenp(VPATH, line, pname, "r");
600	if (ifp == 0) {
601		perror(line);
602		exit(1);
603	}
604	while (fgets(line, BUFSIZ, ifp) != 0) {
605		if (profiling && (strncmp(line, "LIBS=", 5) == 0))
606			fprintf(ofp,"LIBS=${LIBS_P}\n");
607		else
608			fputs(line, ofp);
609	}
610	fclose(ifp);
611}
612
613const char *
614tail(const char *fn)
615{
616	register const char *cp;
617
618	cp = rindex(fn, '/');
619	if (cp == 0)
620		return (fn);
621	return (cp+1);
622}
623
624/*
625 * Create the makerules for each file
626 * which is part of the system.
627 * Devices are processed with the special c2 option -i
628 * which avoids any problem areas with i/o addressing
629 * (e.g. for the VAX); assembler files are processed by as.
630 */
631void
632do_rules(FILE *f)
633{
634	char *cp;
635	char *np, och;
636	const char *tp;
637	register struct file_list *ftp;
638	const char *extras = ""; /* dvw: init to "" */
639	char *source_dir;
640	char och_upper;
641	const char *nl = "";
642
643	for (ftp = ftab; ftp != 0; ftp = ftp->f_next) {
644		if (ftp->f_type == INVISIBLE)
645			continue;
646		cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
647		och = *cp;
648		/*
649			*	Don't compile '.o' files
650			*/
651		if (och == 'o')
652			continue;
653		/*
654			*	Determine where sources should come from
655			*/
656		if ((np[0] == '.') && (np[1] == '/')) {
657			source_dir = "";
658			np += 2;
659		} else
660			source_dir = "$(SOURCE_DIR)/";
661		*cp = '\0';
662		tp = tail(np);	/* dvw: init tp before 'if' */
663		fprintf(f, "-include %sd\n", tp);
664		fprintf(f, "%so: %s%s%c\n", tp, source_dir, np, och);
665		if (och == 's') {
666			fprintf(f, "\t${S_RULE_0}\n");
667			fprintf(f, "\t${S_RULE_1A}%s%.*s${S_RULE_1B}%s\n",
668					source_dir, (int)(tp-np), np, nl);
669			fprintf(f, "\t${S_RULE_2}%s\n", nl);
670			continue;
671		}
672		extras = "";
673		switch (ftp->f_type) {
674
675		case NORMAL:
676			goto common;
677			break;
678
679		case DRIVER:
680			extras = "_D";
681			goto common;
682			break;
683
684		case PROFILING:
685			if (!profiling)
686				continue;
687			if (COPTS == 0) {
688				fprintf(stderr,
689					"config: COPTS undefined in generic makefile");
690				COPTS = "";
691			}
692			extras = "_P";
693			goto common;
694
695		common:
696			och_upper = och + 'A' - 'a';
697			fprintf(f, "\t${%c_RULE_0%s}\n", och_upper, extras);
698			fprintf(f, "\t${%c_RULE_1A%s}", och_upper, extras);
699			if (ftp->f_extra)
700				fprintf(f, "%s", ftp->f_extra);
701			fprintf(f, "%s%.*s${%c_RULE_1B%s}%s\n",
702					source_dir, (int)(tp-np), np, och_upper, extras, nl);
703
704			/* While we are still using CTF, any build that normally does not support CTF will
705			 * a "standard" compile done as well that we can harvest CTF information from; do
706			 * that here.
707			 */
708			fprintf(f, "\t${%c_CTFRULE_1A%s}", och_upper, extras);
709			if (ftp->f_extra)
710				fprintf(f, "%s", ftp->f_extra);
711			fprintf(f, "%s%.*s${%c_CTFRULE_1B%s}%s\n",
712					source_dir, (int)(tp-np), np, och_upper, extras, nl);
713
714			fprintf(f, "\t${%c_RULE_2%s}%s\n", och_upper, extras, nl);
715			fprintf(f, "\t${%c_CTFRULE_2%s}%s\n", och_upper, extras, nl);
716			break;
717
718		default:
719			printf("Don't know rules for %s\n", np);
720			break;
721		}
722		*cp = och;
723	}
724}
725
726char *
727allCaps(str)
728	register char *str;
729{
730	register char *cp = str;
731
732	while (*str) {
733		if (islower(*str))
734			*str = toupper(*str);
735		str++;
736	}
737	return (cp);
738}
739
740#define OLDSALUTATION "# DO NOT DELETE THIS LINE"
741
742#define LINESIZE 1024
743static char makbuf[LINESIZE];		/* one line buffer for makefile */
744
745void
746copy_dependencies(FILE *makin, FILE *makout)
747{
748	register int oldlen = (sizeof OLDSALUTATION - 1);
749
750	while (fgets(makbuf, LINESIZE, makin) != NULL) {
751		if (! strncmp(makbuf, OLDSALUTATION, oldlen))
752			break;
753	}
754	while (fgets(makbuf, LINESIZE, makin) != NULL) {
755		if (oldlen != 0)
756		{
757			if (makbuf[0] == '\n')
758				continue;
759			else
760				oldlen = 0;
761		}
762		fputs(makbuf, makout);
763	}
764}
765