mkmakefile.c revision 160495
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 160495 2006-07-19 10:46:38Z stefanf $";
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	STAILQ_INSERT_TAIL(&ftab, fp, f_next);
102	return (fp);
103}
104
105/*
106 * Build the makefile from the skeleton
107 */
108void
109makefile(void)
110{
111	FILE *ifp, *ofp;
112	char line[BUFSIZ];
113	struct opt *op;
114	int versreq;
115
116	read_files();
117	snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename);
118	ifp = fopen(line, "r");
119	if (ifp == 0) {
120		snprintf(line, sizeof(line), "Makefile.%s", machinename);
121		ifp = fopen(line, "r");
122	}
123	if (ifp == 0)
124		err(1, "%s", line);
125
126	ofp = fopen(path("Makefile.new"), "w");
127	if (ofp == 0)
128		err(1, "%s", path("Makefile.new"));
129	fprintf(ofp, "KERN_IDENT=%s\n", ident);
130	SLIST_FOREACH(op, &mkopt, op_next)
131		fprintf(ofp, "%s=%s\n", op->op_name, op->op_value);
132	if (debugging)
133		fprintf(ofp, "DEBUG=-g\n");
134	if (profiling)
135		fprintf(ofp, "PROFLEVEL=%d\n", profiling);
136	if (*srcdir != '\0')
137		fprintf(ofp,"S=%s\n", srcdir);
138	while (fgets(line, BUFSIZ, ifp) != 0) {
139		if (*line != '%') {
140			fprintf(ofp, "%s", line);
141			continue;
142		}
143		if (eq(line, "%BEFORE_DEPEND\n"))
144			do_before_depend(ofp);
145		else if (eq(line, "%OBJS\n"))
146			do_objs(ofp);
147		else if (strncmp(line, "%FILES.", 7) == 0)
148			do_xxfiles(line, ofp);
149		else if (eq(line, "%RULES\n"))
150			do_rules(ofp);
151		else if (eq(line, "%CLEAN\n"))
152			do_clean(ofp);
153		else if (strncmp(line, "%VERSREQ=", sizeof("%VERSREQ=") - 1) == 0) {
154			versreq = atoi(line + sizeof("%VERSREQ=") - 1);
155			if (MAJOR_VERS(versreq) != MAJOR_VERS(CONFIGVERS) ||
156			    versreq > CONFIGVERS) {
157				fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n");
158				fprintf(stderr, "config version = %d, ", CONFIGVERS);
159				fprintf(stderr, "version required = %d\n\n", versreq);
160				fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n");
161				fprintf(stderr, "with your /usr/src/sys and install a new config binary\n");
162				fprintf(stderr, "before trying this again.\n\n");
163				fprintf(stderr, "If running the new config fails check your config\n");
164				fprintf(stderr, "file against the GENERIC or LINT config files for\n");
165				fprintf(stderr, "changes in config syntax, or option/device naming\n");
166				fprintf(stderr, "conventions\n\n");
167				exit(1);
168			}
169		} else
170			fprintf(stderr,
171			    "Unknown %% construct in generic makefile: %s",
172			    line);
173	}
174	(void) fclose(ifp);
175	(void) fclose(ofp);
176	moveifchanged(path("Makefile.new"), path("Makefile"));
177}
178
179/*
180 * Build hints.c from the skeleton
181 */
182void
183makehints(void)
184{
185	FILE *ifp, *ofp;
186	char line[BUFSIZ];
187	char *s;
188
189	if (hints) {
190		ifp = fopen(hints, "r");
191		if (ifp == NULL)
192			err(1, "%s", hints);
193	} else {
194		ifp = NULL;
195	}
196	ofp = fopen(path("hints.c.new"), "w");
197	if (ofp == NULL)
198		err(1, "%s", path("hints.c.new"));
199	fprintf(ofp, "#include <sys/types.h>\n");
200	fprintf(ofp, "#include <sys/systm.h>\n");
201	fprintf(ofp, "\n");
202	fprintf(ofp, "int hintmode = %d;\n", hintmode);
203	fprintf(ofp, "char static_hints[] = {\n");
204	if (ifp) {
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	}
235	fprintf(ofp, "\"\\0\"\n};\n");
236	if (ifp)
237		fclose(ifp);
238	fclose(ofp);
239	moveifchanged(path("hints.c.new"), path("hints.c"));
240}
241
242/*
243 * Build env.c from the skeleton
244 */
245void
246makeenv(void)
247{
248	FILE *ifp, *ofp;
249	char line[BUFSIZ];
250	char *s;
251
252	if (env) {
253		ifp = fopen(env, "r");
254		if (ifp == NULL)
255			err(1, "%s", env);
256	} else {
257		ifp = NULL;
258	}
259	ofp = fopen(path("env.c.new"), "w");
260	if (ofp == NULL)
261		err(1, "%s", path("env.c.new"));
262	fprintf(ofp, "#include <sys/types.h>\n");
263	fprintf(ofp, "#include <sys/systm.h>\n");
264	fprintf(ofp, "\n");
265	fprintf(ofp, "int envmode = %d;\n", envmode);
266	fprintf(ofp, "char static_env[] = {\n");
267	if (ifp) {
268		while (fgets(line, BUFSIZ, ifp) != 0) {
269			/* zap trailing CR and/or LF */
270			while ((s = rindex(line, '\n')) != NULL)
271				*s = '\0';
272			while ((s = rindex(line, '\r')) != NULL)
273				*s = '\0';
274			/* remove # comments */
275			s = index(line, '#');
276			if (s)
277				*s = '\0';
278			/* remove any whitespace and " characters */
279			s = line;
280			while (*s) {
281				if (*s == ' ' || *s == '\t' || *s == '"') {
282					while (*s) {
283						s[0] = s[1];
284						s++;
285					}
286					/* start over */
287					s = line;
288					continue;
289				}
290				s++;
291			}
292			/* anything left? */
293			if (*line == '\0')
294				continue;
295			fprintf(ofp, "\"%s\\0\"\n", line);
296		}
297	}
298	fprintf(ofp, "\"\\0\"\n};\n");
299	if (ifp)
300		fclose(ifp);
301	fclose(ofp);
302	moveifchanged(path("env.c.new"), path("env.c"));
303}
304
305static void
306read_file(char *fname)
307{
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	 * filename    [ standard | mandatory | optional ]
322	 *	[ dev* [ | dev* ... ] | profiling-routine ] [ no-obj ]
323	 *	[ compile-with "compile rule" [no-implicit-rule] ]
324	 *      [ dependency "dependency-list"] [ before-depend ]
325	 *	[ clean "file-list"] [ warning "text warning" ]
326	 */
327	wd = get_word(fp);
328	if (wd == (char *)EOF) {
329		(void) fclose(fp);
330		return;
331	}
332	if (wd == 0)
333		goto next;
334	if (wd[0] == '#')
335	{
336		while (((wd = get_word(fp)) != (char *)EOF) && wd)
337			;
338		goto next;
339	}
340	this = ns(wd);
341	next_word(fp, wd);
342	if (wd == 0) {
343		printf("%s: No type for %s.\n",
344		    fname, this);
345		exit(1);
346	}
347	tp = fl_lookup(this);
348	compile = 0;
349	match = 1;
350	nreqs = 0;
351	compilewith = 0;
352	depends = 0;
353	clean = 0;
354	warning = 0;
355	std = mandatory = 0;
356	imp_rule = 0;
357	no_obj = 0;
358	before_depend = 0;
359	nowerror = 0;
360	filetype = NORMAL;
361	if (eq(wd, "standard")) {
362		std = 1;
363	/*
364	 * If an entry is marked "mandatory", config will abort if it's
365	 * not called by a configuration line in the config file.  Apart
366	 * from this, the device is handled like one marked "optional".
367	 */
368	} else if (eq(wd, "mandatory")) {
369		mandatory = 1;
370	} else if (!eq(wd, "optional")) {
371		printf("%s: %s must be optional, mandatory or standard\n",
372		       fname, this);
373		exit(1);
374	}
375nextparam:
376	next_word(fp, wd);
377	if (wd == 0) {
378		compile += match;
379		if (compile && tp == NULL)
380			goto doneparam;
381		goto next;
382	}
383	if (eq(wd, "|")) {
384		if (nreqs == 0) {
385			printf("%s: syntax error describing %s\n",
386			    fname, this);
387			exit(1);
388		}
389		compile += match;
390		match = 1;
391		nreqs = 0;
392		goto nextparam;
393	}
394	if (eq(wd, "no-obj")) {
395		no_obj++;
396		goto nextparam;
397	}
398	if (eq(wd, "no-implicit-rule")) {
399		if (compilewith == 0) {
400			printf("%s: alternate rule required when "
401			       "\"no-implicit-rule\" is specified.\n",
402			       fname);
403		}
404		imp_rule++;
405		goto nextparam;
406	}
407	if (eq(wd, "before-depend")) {
408		before_depend++;
409		goto nextparam;
410	}
411	if (eq(wd, "dependency")) {
412		next_quoted_word(fp, wd);
413		if (wd == 0) {
414			printf("%s: %s missing compile command string.\n",
415			       fname, this);
416			exit(1);
417		}
418		depends = ns(wd);
419		goto nextparam;
420	}
421	if (eq(wd, "clean")) {
422		next_quoted_word(fp, wd);
423		if (wd == 0) {
424			printf("%s: %s missing clean file list.\n",
425			       fname, this);
426			exit(1);
427		}
428		clean = ns(wd);
429		goto nextparam;
430	}
431	if (eq(wd, "compile-with")) {
432		next_quoted_word(fp, wd);
433		if (wd == 0) {
434			printf("%s: %s missing compile command string.\n",
435			       fname, this);
436			exit(1);
437		}
438		compilewith = ns(wd);
439		goto nextparam;
440	}
441	if (eq(wd, "warning")) {
442		next_quoted_word(fp, wd);
443		if (wd == 0) {
444			printf("%s: %s missing warning text string.\n",
445				fname, this);
446			exit(1);
447		}
448		warning = ns(wd);
449		goto nextparam;
450	}
451	nreqs++;
452	if (eq(wd, "local")) {
453		filetype = LOCAL;
454		goto nextparam;
455	}
456	if (eq(wd, "no-depend")) {
457		filetype = NODEPEND;
458		goto nextparam;
459	}
460	if (eq(wd, "profiling-routine")) {
461		filetype = PROFILING;
462		goto nextparam;
463	}
464	if (eq(wd, "nowerror")) {
465		nowerror = 1;
466		goto nextparam;
467	}
468	STAILQ_FOREACH(dp, &dtab, d_next)
469		if (eq(dp->d_name, wd)) {
470			dp->d_done |= DEVDONE;
471			goto nextparam;
472		}
473	if (mandatory) {
474		printf("%s: mandatory device \"%s\" not found\n",
475		       fname, wd);
476		exit(1);
477	}
478	if (std) {
479		printf("standard entry %s has a device keyword - %s!\n",
480		       this, wd);
481		exit(1);
482	}
483	SLIST_FOREACH(op, &opt, op_next)
484		if (op->op_value == 0 && opteq(op->op_name, wd))
485			goto nextparam;
486	match = 0;
487	goto nextparam;
488
489doneparam:
490	if (std == 0 && nreqs == 0) {
491		printf("%s: what is %s optional on?\n",
492		    fname, this);
493		exit(1);
494	}
495
496	if (wd) {
497		printf("%s: syntax error describing %s\n",
498		    fname, this);
499		exit(1);
500	}
501	if (filetype == PROFILING && profiling == 0)
502		goto next;
503	tp = new_fent();
504	tp->f_fn = this;
505	tp->f_type = filetype;
506	if (imp_rule)
507		tp->f_flags |= NO_IMPLCT_RULE;
508	if (no_obj)
509		tp->f_flags |= NO_OBJ;
510	if (before_depend)
511		tp->f_flags |= BEFORE_DEPEND;
512	if (nowerror)
513		tp->f_flags |= NOWERROR;
514	tp->f_compilewith = compilewith;
515	tp->f_depends = depends;
516	tp->f_clean = clean;
517	tp->f_warn = warning;
518	goto next;
519}
520
521/*
522 * Read in the information about files used in making the system.
523 * Store it in the ftab linked list.
524 */
525static void
526read_files(void)
527{
528	char fname[MAXPATHLEN];
529	struct files_name *nl, *tnl;
530
531	(void) snprintf(fname, sizeof(fname), "../../conf/files");
532	read_file(fname);
533	(void) snprintf(fname, sizeof(fname),
534		       	"../../conf/files.%s", machinename);
535	read_file(fname);
536	for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) {
537		read_file(nl->f_name);
538		tnl = STAILQ_NEXT(nl, f_next);
539		free(nl->f_name);
540		free(nl);
541	}
542}
543
544static int
545opteq(const char *cp, const char *dp)
546{
547	char c, d;
548
549	for (; ; cp++, dp++) {
550		if (*cp != *dp) {
551			c = isupper(*cp) ? tolower(*cp) : *cp;
552			d = isupper(*dp) ? tolower(*dp) : *dp;
553			if (c != d)
554				return (0);
555		}
556		if (*cp == 0)
557			return (1);
558	}
559}
560
561static void
562do_before_depend(FILE *fp)
563{
564	struct file_list *tp;
565	int lpos, len;
566
567	fputs("BEFORE_DEPEND=", fp);
568	lpos = 15;
569	STAILQ_FOREACH(tp, &ftab, f_next)
570		if (tp->f_flags & BEFORE_DEPEND) {
571			len = strlen(tp->f_fn);
572			if ((len = 3 + len) + lpos > 72) {
573				lpos = 8;
574				fputs("\\\n\t", fp);
575			}
576			if (tp->f_flags & NO_IMPLCT_RULE)
577				fprintf(fp, "%s ", tp->f_fn);
578			else
579				fprintf(fp, "$S/%s ", tp->f_fn);
580			lpos += len + 1;
581		}
582	if (lpos != 8)
583		putc('\n', fp);
584}
585
586static void
587do_objs(FILE *fp)
588{
589	struct file_list *tp;
590	int lpos, len;
591	char *cp, och, *sp;
592
593	fprintf(fp, "OBJS=");
594	lpos = 6;
595	STAILQ_FOREACH(tp, &ftab, f_next) {
596		if (tp->f_flags & NO_OBJ)
597			continue;
598		sp = tail(tp->f_fn);
599		cp = sp + (len = strlen(sp)) - 1;
600		och = *cp;
601		*cp = 'o';
602		if (len + lpos > 72) {
603			lpos = 8;
604			fprintf(fp, "\\\n\t");
605		}
606		fprintf(fp, "%s ", sp);
607		lpos += len + 1;
608		*cp = och;
609	}
610	if (lpos != 8)
611		putc('\n', fp);
612}
613
614static void
615do_xxfiles(char *tag, FILE *fp)
616{
617	struct file_list *tp;
618	int lpos, len, slen;
619	char *suff, *SUFF;
620
621	if (tag[strlen(tag) - 1] == '\n')
622		tag[strlen(tag) - 1] = '\0';
623
624	suff = ns(tag + 7);
625	SUFF = ns(suff);
626	raisestr(SUFF);
627	slen = strlen(suff);
628
629	fprintf(fp, "%sFILES=", SUFF);
630	lpos = 8;
631	STAILQ_FOREACH(tp, &ftab, f_next)
632		if (tp->f_type != NODEPEND) {
633			len = strlen(tp->f_fn);
634			if (tp->f_fn[len - slen - 1] != '.')
635				continue;
636			if (strcasecmp(&tp->f_fn[len - slen], suff) != 0)
637				continue;
638			if ((len = 3 + len) + lpos > 72) {
639				lpos = 8;
640				fputs("\\\n\t", fp);
641			}
642			if (tp->f_type != LOCAL)
643				fprintf(fp, "$S/%s ", tp->f_fn);
644			else
645				fprintf(fp, "%s ", tp->f_fn);
646			lpos += len + 1;
647		}
648	if (lpos != 8)
649		putc('\n', fp);
650}
651
652static char *
653tail(char *fn)
654{
655	char *cp;
656
657	cp = rindex(fn, '/');
658	if (cp == 0)
659		return (fn);
660	return (cp+1);
661}
662
663/*
664 * Create the makerules for each file
665 * which is part of the system.
666 */
667static void
668do_rules(FILE *f)
669{
670	char *cp, *np, och;
671	struct file_list *ftp;
672	char *compilewith;
673
674	STAILQ_FOREACH(ftp, &ftab, f_next) {
675		if (ftp->f_warn)
676			printf("WARNING: %s\n", ftp->f_warn);
677		cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
678		och = *cp;
679		if (ftp->f_flags & NO_IMPLCT_RULE) {
680			if (ftp->f_depends)
681				fprintf(f, "%s: %s\n", np, ftp->f_depends);
682			else
683				fprintf(f, "%s: \n", np);
684		}
685		else {
686			*cp = '\0';
687			if (och == 'o') {
688				fprintf(f, "%so:\n\t-cp $S/%so .\n\n",
689					tail(np), np);
690				continue;
691			}
692			if (ftp->f_depends) {
693				fprintf(f, "%sln: $S/%s%c %s\n", tail(np),
694					np, och, ftp->f_depends);
695				fprintf(f, "\t${NORMAL_LINT}\n\n");
696				fprintf(f, "%so: $S/%s%c %s\n", tail(np),
697					np, och, ftp->f_depends);
698			}
699			else {
700				fprintf(f, "%sln: $S/%s%c\n", tail(np),
701					np, och);
702				fprintf(f, "\t${NORMAL_LINT}\n\n");
703				fprintf(f, "%so: $S/%s%c\n", tail(np),
704					np, och);
705			}
706		}
707		compilewith = ftp->f_compilewith;
708		if (compilewith == 0) {
709			const char *ftype = NULL;
710			static char cmd[128];
711
712			switch (ftp->f_type) {
713
714			case NORMAL:
715				ftype = "NORMAL";
716				break;
717
718			case PROFILING:
719				if (!profiling)
720					continue;
721				ftype = "PROFILE";
722				break;
723
724			default:
725				printf("config: don't know rules for %s\n", np);
726				break;
727			}
728			snprintf(cmd, sizeof(cmd), "${%s_%c%s}", ftype,
729			    toupper(och),
730			    ftp->f_flags & NOWERROR ? "_NOWERROR" : "");
731			compilewith = cmd;
732		}
733		*cp = och;
734		fprintf(f, "\t%s\n\n", compilewith);
735	}
736}
737
738static void
739do_clean(FILE *fp)
740{
741	struct file_list *tp;
742	int lpos, len;
743
744	fputs("CLEAN=", fp);
745	lpos = 7;
746	STAILQ_FOREACH(tp, &ftab, f_next)
747		if (tp->f_clean) {
748			len = strlen(tp->f_clean);
749			if (len + lpos > 72) {
750				lpos = 8;
751				fputs("\\\n\t", fp);
752			}
753			fprintf(fp, "%s ", tp->f_clean);
754			lpos += len + 1;
755		}
756	if (lpos != 8)
757		putc('\n', fp);
758}
759
760char *
761raisestr(char *str)
762{
763	char *cp = str;
764
765	while (*str) {
766		if (islower(*str))
767			*str = toupper(*str);
768		str++;
769	}
770	return (cp);
771}
772