mkmakefile.c revision 209135
1/*
2 * Copyright (c) 1993, 19801990
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31#if 0
32static char sccsid[] = "@(#)mkmakefile.c	8.1 (Berkeley) 6/6/93";
33#endif
34static const char rcsid[] =
35  "$FreeBSD: head/usr.sbin/config/mkmakefile.c 209135 2010-06-13 16:54:11Z imp $";
36#endif /* not lint */
37
38/*
39 * Build the makefile for the system, from
40 * the information in the files files and the
41 * additional files for the machine being compiled to.
42 */
43
44#include <ctype.h>
45#include <err.h>
46#include <stdio.h>
47#include <string.h>
48#include <sys/param.h>
49#include "y.tab.h"
50#include "config.h"
51#include "configvers.h"
52
53#define next_word(fp, wd) \
54	{ char *word = get_word(fp); \
55	  if (word == (char *)EOF) \
56		return; \
57	  else \
58		wd = word; \
59	}
60#define next_quoted_word(fp, wd) \
61	{ char *word = get_quoted_word(fp); \
62	  if (word == (char *)EOF) \
63		return; \
64	  else \
65		wd = word; \
66	}
67
68static char *tail(char *);
69static void do_clean(FILE *);
70static void do_rules(FILE *);
71static void do_xxfiles(char *, FILE *);
72static void do_objs(FILE *);
73static void do_before_depend(FILE *);
74static int opteq(const char *, const char *);
75static void read_files(void);
76
77/*
78 * Lookup a file, by name.
79 */
80static struct file_list *
81fl_lookup(char *file)
82{
83	struct file_list *fp;
84
85	STAILQ_FOREACH(fp, &ftab, f_next) {
86		if (eq(fp->f_fn, file))
87			return (fp);
88	}
89	return (0);
90}
91
92/*
93 * Make a new file list entry
94 */
95static struct file_list *
96new_fent(void)
97{
98	struct file_list *fp;
99
100	fp = (struct file_list *) calloc(1, sizeof *fp);
101	if (fp == NULL)
102		err(EXIT_FAILURE, "calloc");
103	STAILQ_INSERT_TAIL(&ftab, fp, f_next);
104	return (fp);
105}
106
107/*
108 * Open the correct Makefile and return it, or error out.
109 */
110FILE *
111open_makefile_template(void)
112{
113	FILE *ifp;
114	char line[BUFSIZ];
115
116	snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename);
117	ifp = fopen(line, "r");
118	if (ifp == 0) {
119		snprintf(line, sizeof(line), "Makefile.%s", machinename);
120		ifp = fopen(line, "r");
121	}
122	if (ifp == 0)
123		err(1, "%s", line);
124	return (ifp);
125}
126
127/*
128 * Build the makefile from the skeleton
129 */
130void
131makefile(void)
132{
133	FILE *ifp, *ofp;
134	char line[BUFSIZ];
135	struct opt *op, *t;
136
137	read_files();
138	ifp = open_makefile_template();
139	ofp = fopen(path("Makefile.new"), "w");
140	if (ofp == 0)
141		err(1, "%s", path("Makefile.new"));
142	fprintf(ofp, "KERN_IDENT=%s\n", ident);
143	SLIST_FOREACH_SAFE(op, &mkopt, op_next, t) {
144		fprintf(ofp, "%s=%s", op->op_name, op->op_value);
145		while ((op = SLIST_NEXT(op, op_append)) != NULL)
146			fprintf(ofp, " %s", op->op_value);
147		fprintf(ofp, "\n");
148	}
149	if (debugging)
150		fprintf(ofp, "DEBUG=-g\n");
151	if (profiling)
152		fprintf(ofp, "PROFLEVEL=%d\n", profiling);
153	if (*srcdir != '\0')
154		fprintf(ofp,"S=%s\n", srcdir);
155	while (fgets(line, BUFSIZ, ifp) != 0) {
156		if (*line != '%') {
157			fprintf(ofp, "%s", line);
158			continue;
159		}
160		if (eq(line, "%BEFORE_DEPEND\n"))
161			do_before_depend(ofp);
162		else if (eq(line, "%OBJS\n"))
163			do_objs(ofp);
164		else if (strncmp(line, "%FILES.", 7) == 0)
165			do_xxfiles(line, ofp);
166		else if (eq(line, "%RULES\n"))
167			do_rules(ofp);
168		else if (eq(line, "%CLEAN\n"))
169			do_clean(ofp);
170		else if (strncmp(line, "%VERSREQ=", 9) == 0)
171			line[0] = '\0'; /* handled elsewhere */
172		else
173			fprintf(stderr,
174			    "Unknown %% construct in generic makefile: %s",
175			    line);
176	}
177	(void) fclose(ifp);
178	(void) fclose(ofp);
179	moveifchanged(path("Makefile.new"), path("Makefile"));
180}
181
182/*
183 * Build hints.c from the skeleton
184 */
185void
186makehints(void)
187{
188	FILE *ifp, *ofp;
189	char line[BUFSIZ];
190	char *s;
191	struct hint *hint;
192
193	ofp = fopen(path("hints.c.new"), "w");
194	if (ofp == NULL)
195		err(1, "%s", path("hints.c.new"));
196	fprintf(ofp, "#include <sys/types.h>\n");
197	fprintf(ofp, "#include <sys/systm.h>\n");
198	fprintf(ofp, "\n");
199	fprintf(ofp, "int hintmode = %d;\n", hintmode);
200	fprintf(ofp, "char static_hints[] = {\n");
201	STAILQ_FOREACH(hint, &hints, hint_next) {
202		ifp = fopen(hint->hint_name, "r");
203		if (ifp == NULL)
204			err(1, "%s", hint->hint_name);
205		while (fgets(line, BUFSIZ, ifp) != 0) {
206			/* zap trailing CR and/or LF */
207			while ((s = rindex(line, '\n')) != NULL)
208				*s = '\0';
209			while ((s = rindex(line, '\r')) != NULL)
210				*s = '\0';
211			/* remove # comments */
212			s = index(line, '#');
213			if (s)
214				*s = '\0';
215			/* remove any whitespace and " characters */
216			s = line;
217			while (*s) {
218				if (*s == ' ' || *s == '\t' || *s == '"') {
219					while (*s) {
220						s[0] = s[1];
221						s++;
222					}
223					/* start over */
224					s = line;
225					continue;
226				}
227				s++;
228			}
229			/* anything left? */
230			if (*line == '\0')
231				continue;
232			fprintf(ofp, "\"%s\\0\"\n", line);
233		}
234		fclose(ifp);
235	}
236	fprintf(ofp, "\"\\0\"\n};\n");
237	fclose(ofp);
238	moveifchanged(path("hints.c.new"), path("hints.c"));
239}
240
241/*
242 * Build env.c from the skeleton
243 */
244void
245makeenv(void)
246{
247	FILE *ifp, *ofp;
248	char line[BUFSIZ];
249	char *s;
250
251	if (env) {
252		ifp = fopen(env, "r");
253		if (ifp == NULL)
254			err(1, "%s", env);
255	} else {
256		ifp = NULL;
257	}
258	ofp = fopen(path("env.c.new"), "w");
259	if (ofp == NULL)
260		err(1, "%s", path("env.c.new"));
261	fprintf(ofp, "#include <sys/types.h>\n");
262	fprintf(ofp, "#include <sys/systm.h>\n");
263	fprintf(ofp, "\n");
264	fprintf(ofp, "int envmode = %d;\n", envmode);
265	fprintf(ofp, "char static_env[] = {\n");
266	if (ifp) {
267		while (fgets(line, BUFSIZ, ifp) != 0) {
268			/* zap trailing CR and/or LF */
269			while ((s = rindex(line, '\n')) != NULL)
270				*s = '\0';
271			while ((s = rindex(line, '\r')) != NULL)
272				*s = '\0';
273			/* remove # comments */
274			s = index(line, '#');
275			if (s)
276				*s = '\0';
277			/* remove any whitespace and " characters */
278			s = line;
279			while (*s) {
280				if (*s == ' ' || *s == '\t' || *s == '"') {
281					while (*s) {
282						s[0] = s[1];
283						s++;
284					}
285					/* start over */
286					s = line;
287					continue;
288				}
289				s++;
290			}
291			/* anything left? */
292			if (*line == '\0')
293				continue;
294			fprintf(ofp, "\"%s\\0\"\n", line);
295		}
296	}
297	fprintf(ofp, "\"\\0\"\n};\n");
298	if (ifp)
299		fclose(ifp);
300	fclose(ofp);
301	moveifchanged(path("env.c.new"), path("env.c"));
302}
303
304static void
305read_file(char *fname)
306{
307	char ifname[MAXPATHLEN];
308	FILE *fp;
309	struct file_list *tp;
310	struct device *dp;
311	struct opt *op;
312	char *wd, *this, *compilewith, *depends, *clean, *warning;
313	int compile, match, nreqs, std, filetype,
314	    imp_rule, no_obj, before_depend, mandatory, nowerror;
315
316	fp = fopen(fname, "r");
317	if (fp == 0)
318		err(1, "%s", fname);
319next:
320	/*
321	 * include "filename"
322	 * filename    [ standard | mandatory | optional ]
323	 *	[ dev* [ | dev* ... ] | profiling-routine ] [ no-obj ]
324	 *	[ compile-with "compile rule" [no-implicit-rule] ]
325	 *      [ dependency "dependency-list"] [ before-depend ]
326	 *	[ clean "file-list"] [ warning "text warning" ]
327	 */
328	wd = get_word(fp);
329	if (wd == (char *)EOF) {
330		(void) fclose(fp);
331		return;
332	}
333	if (wd == 0)
334		goto next;
335	if (wd[0] == '#')
336	{
337		while (((wd = get_word(fp)) != (char *)EOF) && wd)
338			;
339		goto next;
340	}
341	if (eq(wd, "include")) {
342		next_quoted_word(fp, wd);
343		if (wd == 0) {
344			printf("%s: missing include filename.\n", fname);
345			exit(1);
346		}
347		(void) snprintf(ifname, sizeof(ifname), "../../%s", wd);
348		read_file(ifname);
349		while (((wd = get_word(fp)) != (char *)EOF) && wd)
350			;
351		goto next;
352	}
353	this = ns(wd);
354	next_word(fp, wd);
355	if (wd == 0) {
356		printf("%s: No type for %s.\n",
357		    fname, this);
358		exit(1);
359	}
360	tp = fl_lookup(this);
361	compile = 0;
362	match = 1;
363	nreqs = 0;
364	compilewith = 0;
365	depends = 0;
366	clean = 0;
367	warning = 0;
368	std = mandatory = 0;
369	imp_rule = 0;
370	no_obj = 0;
371	before_depend = 0;
372	nowerror = 0;
373	filetype = NORMAL;
374	if (eq(wd, "standard")) {
375		std = 1;
376	/*
377	 * If an entry is marked "mandatory", config will abort if it's
378	 * not called by a configuration line in the config file.  Apart
379	 * from this, the device is handled like one marked "optional".
380	 */
381	} else if (eq(wd, "mandatory")) {
382		mandatory = 1;
383	} else if (!eq(wd, "optional")) {
384		printf("%s: %s must be optional, mandatory or standard\n",
385		       fname, this);
386		exit(1);
387	}
388nextparam:
389	next_word(fp, wd);
390	if (wd == 0) {
391		compile += match;
392		if (compile && tp == NULL)
393			goto doneparam;
394		goto next;
395	}
396	if (eq(wd, "|")) {
397		if (nreqs == 0) {
398			printf("%s: syntax error describing %s\n",
399			    fname, this);
400			exit(1);
401		}
402		compile += match;
403		match = 1;
404		nreqs = 0;
405		goto nextparam;
406	}
407	if (eq(wd, "no-obj")) {
408		no_obj++;
409		goto nextparam;
410	}
411	if (eq(wd, "no-implicit-rule")) {
412		if (compilewith == 0) {
413			printf("%s: alternate rule required when "
414			       "\"no-implicit-rule\" is specified.\n",
415			       fname);
416		}
417		imp_rule++;
418		goto nextparam;
419	}
420	if (eq(wd, "before-depend")) {
421		before_depend++;
422		goto nextparam;
423	}
424	if (eq(wd, "dependency")) {
425		next_quoted_word(fp, wd);
426		if (wd == 0) {
427			printf("%s: %s missing compile command string.\n",
428			       fname, this);
429			exit(1);
430		}
431		depends = ns(wd);
432		goto nextparam;
433	}
434	if (eq(wd, "clean")) {
435		next_quoted_word(fp, wd);
436		if (wd == 0) {
437			printf("%s: %s missing clean file list.\n",
438			       fname, this);
439			exit(1);
440		}
441		clean = ns(wd);
442		goto nextparam;
443	}
444	if (eq(wd, "compile-with")) {
445		next_quoted_word(fp, wd);
446		if (wd == 0) {
447			printf("%s: %s missing compile command string.\n",
448			       fname, this);
449			exit(1);
450		}
451		compilewith = ns(wd);
452		goto nextparam;
453	}
454	if (eq(wd, "warning")) {
455		next_quoted_word(fp, wd);
456		if (wd == 0) {
457			printf("%s: %s missing warning text string.\n",
458				fname, this);
459			exit(1);
460		}
461		warning = ns(wd);
462		goto nextparam;
463	}
464	nreqs++;
465	if (eq(wd, "local")) {
466		filetype = LOCAL;
467		goto nextparam;
468	}
469	if (eq(wd, "no-depend")) {
470		filetype = NODEPEND;
471		goto nextparam;
472	}
473	if (eq(wd, "profiling-routine")) {
474		filetype = PROFILING;
475		goto nextparam;
476	}
477	if (eq(wd, "nowerror")) {
478		nowerror = 1;
479		goto nextparam;
480	}
481	STAILQ_FOREACH(dp, &dtab, d_next)
482		if (eq(dp->d_name, wd)) {
483			dp->d_done |= DEVDONE;
484			goto nextparam;
485		}
486	if (mandatory) {
487		printf("%s: mandatory device \"%s\" not found\n",
488		       fname, wd);
489		exit(1);
490	}
491	if (std) {
492		printf("standard entry %s has a device keyword - %s!\n",
493		       this, wd);
494		exit(1);
495	}
496	SLIST_FOREACH(op, &opt, op_next)
497		if (op->op_value == 0 && opteq(op->op_name, wd))
498			goto nextparam;
499	match = 0;
500	goto nextparam;
501
502doneparam:
503	if (std == 0 && nreqs == 0) {
504		printf("%s: what is %s optional on?\n",
505		    fname, this);
506		exit(1);
507	}
508
509	if (wd) {
510		printf("%s: syntax error describing %s\n",
511		    fname, this);
512		exit(1);
513	}
514	if (filetype == PROFILING && profiling == 0)
515		goto next;
516	tp = new_fent();
517	tp->f_fn = this;
518	tp->f_type = filetype;
519	if (imp_rule)
520		tp->f_flags |= NO_IMPLCT_RULE;
521	if (no_obj)
522		tp->f_flags |= NO_OBJ;
523	if (before_depend)
524		tp->f_flags |= BEFORE_DEPEND;
525	if (nowerror)
526		tp->f_flags |= NOWERROR;
527	tp->f_compilewith = compilewith;
528	tp->f_depends = depends;
529	tp->f_clean = clean;
530	tp->f_warn = warning;
531	goto next;
532}
533
534/*
535 * Read in the information about files used in making the system.
536 * Store it in the ftab linked list.
537 */
538static void
539read_files(void)
540{
541	char fname[MAXPATHLEN];
542	struct files_name *nl, *tnl;
543
544	(void) snprintf(fname, sizeof(fname), "../../conf/files");
545	read_file(fname);
546	(void) snprintf(fname, sizeof(fname),
547		       	"../../conf/files.%s", machinename);
548	read_file(fname);
549	for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) {
550		read_file(nl->f_name);
551		tnl = STAILQ_NEXT(nl, f_next);
552		free(nl->f_name);
553		free(nl);
554	}
555}
556
557static int
558opteq(const char *cp, const char *dp)
559{
560	char c, d;
561
562	for (; ; cp++, dp++) {
563		if (*cp != *dp) {
564			c = isupper(*cp) ? tolower(*cp) : *cp;
565			d = isupper(*dp) ? tolower(*dp) : *dp;
566			if (c != d)
567				return (0);
568		}
569		if (*cp == 0)
570			return (1);
571	}
572}
573
574static void
575do_before_depend(FILE *fp)
576{
577	struct file_list *tp;
578	int lpos, len;
579
580	fputs("BEFORE_DEPEND=", fp);
581	lpos = 15;
582	STAILQ_FOREACH(tp, &ftab, f_next)
583		if (tp->f_flags & BEFORE_DEPEND) {
584			len = strlen(tp->f_fn);
585			if ((len = 3 + len) + lpos > 72) {
586				lpos = 8;
587				fputs("\\\n\t", fp);
588			}
589			if (tp->f_flags & NO_IMPLCT_RULE)
590				fprintf(fp, "%s ", tp->f_fn);
591			else
592				fprintf(fp, "$S/%s ", tp->f_fn);
593			lpos += len + 1;
594		}
595	if (lpos != 8)
596		putc('\n', fp);
597}
598
599static void
600do_objs(FILE *fp)
601{
602	struct file_list *tp;
603	int lpos, len;
604	char *cp, och, *sp;
605
606	fprintf(fp, "OBJS=");
607	lpos = 6;
608	STAILQ_FOREACH(tp, &ftab, f_next) {
609		if (tp->f_flags & NO_OBJ)
610			continue;
611		sp = tail(tp->f_fn);
612		cp = sp + (len = strlen(sp)) - 1;
613		och = *cp;
614		*cp = 'o';
615		if (len + lpos > 72) {
616			lpos = 8;
617			fprintf(fp, "\\\n\t");
618		}
619		fprintf(fp, "%s ", sp);
620		lpos += len + 1;
621		*cp = och;
622	}
623	if (lpos != 8)
624		putc('\n', fp);
625}
626
627static void
628do_xxfiles(char *tag, FILE *fp)
629{
630	struct file_list *tp;
631	int lpos, len, slen;
632	char *suff, *SUFF;
633
634	if (tag[strlen(tag) - 1] == '\n')
635		tag[strlen(tag) - 1] = '\0';
636
637	suff = ns(tag + 7);
638	SUFF = ns(suff);
639	raisestr(SUFF);
640	slen = strlen(suff);
641
642	fprintf(fp, "%sFILES=", SUFF);
643	lpos = 8;
644	STAILQ_FOREACH(tp, &ftab, f_next)
645		if (tp->f_type != NODEPEND) {
646			len = strlen(tp->f_fn);
647			if (tp->f_fn[len - slen - 1] != '.')
648				continue;
649			if (strcasecmp(&tp->f_fn[len - slen], suff) != 0)
650				continue;
651			if ((len = 3 + len) + lpos > 72) {
652				lpos = 8;
653				fputs("\\\n\t", fp);
654			}
655			if (tp->f_type != LOCAL)
656				fprintf(fp, "$S/%s ", tp->f_fn);
657			else
658				fprintf(fp, "%s ", tp->f_fn);
659			lpos += len + 1;
660		}
661	if (lpos != 8)
662		putc('\n', fp);
663}
664
665static char *
666tail(char *fn)
667{
668	char *cp;
669
670	cp = rindex(fn, '/');
671	if (cp == 0)
672		return (fn);
673	return (cp+1);
674}
675
676/*
677 * Create the makerules for each file
678 * which is part of the system.
679 */
680static void
681do_rules(FILE *f)
682{
683	char *cp, *np, och;
684	struct file_list *ftp;
685	char *compilewith;
686	char cmd[128];
687
688	STAILQ_FOREACH(ftp, &ftab, f_next) {
689		if (ftp->f_warn)
690			printf("WARNING: %s\n", ftp->f_warn);
691		cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
692		och = *cp;
693		if (ftp->f_flags & NO_IMPLCT_RULE) {
694			if (ftp->f_depends)
695				fprintf(f, "%s: %s\n", np, ftp->f_depends);
696			else
697				fprintf(f, "%s: \n", np);
698		}
699		else {
700			*cp = '\0';
701			if (och == 'o') {
702				fprintf(f, "%so:\n\t-cp $S/%so .\n\n",
703					tail(np), np);
704				continue;
705			}
706			if (ftp->f_depends) {
707				fprintf(f, "%sln: $S/%s%c %s\n", tail(np),
708					np, och, ftp->f_depends);
709				fprintf(f, "\t${NORMAL_LINT}\n\n");
710				fprintf(f, "%so: $S/%s%c %s\n", tail(np),
711					np, och, ftp->f_depends);
712			}
713			else {
714				fprintf(f, "%sln: $S/%s%c\n", tail(np),
715					np, och);
716				fprintf(f, "\t${NORMAL_LINT}\n\n");
717				fprintf(f, "%so: $S/%s%c\n", tail(np),
718					np, och);
719			}
720		}
721		compilewith = ftp->f_compilewith;
722		if (compilewith == 0) {
723			const char *ftype = NULL;
724
725			switch (ftp->f_type) {
726			case NORMAL:
727				ftype = "NORMAL";
728				break;
729			case PROFILING:
730				if (!profiling)
731					continue;
732				ftype = "PROFILE";
733				break;
734			default:
735				printf("config: don't know rules for %s\n", np);
736				break;
737			}
738			snprintf(cmd, sizeof(cmd),
739			    "${%s_%c%s}\n\t@${NORMAL_CTFCONVERT}", ftype,
740			    toupper(och),
741			    ftp->f_flags & NOWERROR ? "_NOWERROR" : "");
742			compilewith = cmd;
743		}
744		*cp = och;
745		fprintf(f, "\t%s\n\n", compilewith);
746	}
747}
748
749static void
750do_clean(FILE *fp)
751{
752	struct file_list *tp;
753	int lpos, len;
754
755	fputs("CLEAN=", fp);
756	lpos = 7;
757	STAILQ_FOREACH(tp, &ftab, f_next)
758		if (tp->f_clean) {
759			len = strlen(tp->f_clean);
760			if (len + lpos > 72) {
761				lpos = 8;
762				fputs("\\\n\t", fp);
763			}
764			fprintf(fp, "%s ", tp->f_clean);
765			lpos += len + 1;
766		}
767	if (lpos != 8)
768		putc('\n', fp);
769}
770
771char *
772raisestr(char *str)
773{
774	char *cp = str;
775
776	while (*str) {
777		if (islower(*str))
778			*str = toupper(*str);
779		str++;
780	}
781	return (cp);
782}
783