xref: /src/tools/build/options/makeman.lua (revision 2619d439fa860db734dbbf4b153c5ac9b15c7e49)
18378665fSKyle Evans--
28378665fSKyle Evans-- Copyright (c) 2023 Kyle Evans <kevans@FreeBSD.org>
38378665fSKyle Evans--
48378665fSKyle Evans-- SPDX-License-Identifier: BSD-2-Clause
58378665fSKyle Evans--
68378665fSKyle Evans
78378665fSKyle Evanslocal libgen = require('posix.libgen')
88378665fSKyle Evanslocal lfs = require('lfs')
98378665fSKyle Evanslocal stdlib = require('posix.stdlib')
108378665fSKyle Evanslocal unistd = require('posix.unistd')
118378665fSKyle Evanslocal sys_wait = require('posix.sys.wait')
128378665fSKyle Evans
138378665fSKyle Evanslocal curdate = os.date("%B %e, %Y")
148378665fSKyle Evans
158378665fSKyle Evanslocal output_head <const> = ".\\\" DO NOT EDIT-- this file is @" .. [[generated by tools/build/options/makeman.
168378665fSKyle Evans.Dd ]] .. curdate .. [[
178378665fSKyle Evans
188378665fSKyle Evans.Dt SRC.CONF 5
198378665fSKyle Evans.Os
208378665fSKyle Evans.Sh NAME
218378665fSKyle Evans.Nm src.conf
228378665fSKyle Evans.Nd "source build options"
238378665fSKyle Evans.Sh DESCRIPTION
248378665fSKyle EvansThe
258378665fSKyle Evans.Nm
268378665fSKyle Evansfile contains variables that control what components will be generated during
278378665fSKyle Evansthe build process of the
288378665fSKyle Evans.Fx
298378665fSKyle Evanssource tree; see
308378665fSKyle Evans.Xr build 7 .
318378665fSKyle Evans.Pp
328378665fSKyle EvansThe
338378665fSKyle Evans.Nm
348378665fSKyle Evansfile uses the standard makefile syntax.
358378665fSKyle EvansHowever,
368378665fSKyle Evans.Nm
378378665fSKyle Evansshould not specify any dependencies to
388378665fSKyle Evans.Xr make 1 .
398378665fSKyle EvansInstead,
408378665fSKyle Evans.Nm
418378665fSKyle Evansis to set
428378665fSKyle Evans.Xr make 1
438378665fSKyle Evansvariables that control the aspects of how the system builds.
448378665fSKyle Evans.Pp
458378665fSKyle EvansThe default location of
468378665fSKyle Evans.Nm
472619d439SEd Masteis the top level of the source tree, or
482619d439SEd Maste.Pa /etc/src.conf
492619d439SEd Masteif no
502619d439SEd Maste.Nm
512619d439SEd Masteis found in the source tree itself,
528378665fSKyle Evansthough an alternative location can be specified in the
538378665fSKyle Evans.Xr make 1
548378665fSKyle Evansvariable
558378665fSKyle Evans.Va SRCCONF .
568378665fSKyle EvansOverriding the location of
578378665fSKyle Evans.Nm
588378665fSKyle Evansmay be necessary if the system-wide settings are not suitable
598378665fSKyle Evansfor a particular build.
608378665fSKyle EvansFor instance, setting
618378665fSKyle Evans.Va SRCCONF
628378665fSKyle Evansto
638378665fSKyle Evans.Pa /dev/null
648378665fSKyle Evanseffectively resets all build controls to their defaults.
658378665fSKyle Evans.Pp
668378665fSKyle EvansThe only purpose of
678378665fSKyle Evans.Nm
688378665fSKyle Evansis to control the compilation of the
698378665fSKyle Evans.Fx
708378665fSKyle Evanssource code, which is usually located in
718378665fSKyle Evans.Pa /usr/src .
728378665fSKyle EvansAs a rule, the system administrator creates
738378665fSKyle Evans.Nm
748378665fSKyle Evanswhen the values of certain control variables need to be changed
758378665fSKyle Evansfrom their defaults.
768378665fSKyle Evans.Pp
778378665fSKyle EvansIn addition, control variables can be specified
788378665fSKyle Evansfor a particular build via the
798378665fSKyle Evans.Fl D
808378665fSKyle Evansoption of
818378665fSKyle Evans.Xr make 1
828378665fSKyle Evansor in its environment; see
838378665fSKyle Evans.Xr environ 7 .
848378665fSKyle Evans.Pp
858378665fSKyle EvansThe environment of
868378665fSKyle Evans.Xr make 1
878378665fSKyle Evansfor the build can be controlled via the
888378665fSKyle Evans.Va SRC_ENV_CONF
898378665fSKyle Evansvariable, which defaults to
908378665fSKyle Evans.Pa /etc/src-env.conf .
918378665fSKyle EvansSome examples that may only be set in this file are
928378665fSKyle Evans.Va WITH_DIRDEPS_BUILD ,
938378665fSKyle Evansand
948378665fSKyle Evans.Va WITH_META_MODE ,
958378665fSKyle Evansand
968378665fSKyle Evans.Va MAKEOBJDIRPREFIX
978378665fSKyle Evansas they are environment-only variables.
988378665fSKyle Evans.Pp
998378665fSKyle EvansThe values of
1008378665fSKyle Evans.Va WITH_
1018378665fSKyle Evansand
1028378665fSKyle Evans.Va WITHOUT_
1038378665fSKyle Evansvariables are ignored regardless of their setting;
1048378665fSKyle Evanseven if they would be set to
1058378665fSKyle Evans.Dq Li FALSE
1068378665fSKyle Evansor
1078378665fSKyle Evans.Dq Li NO .
1088378665fSKyle EvansThe presence of an option causes
1098378665fSKyle Evansit to be honored by
1108378665fSKyle Evans.Xr make 1 .
1118378665fSKyle Evans.Pp
1128378665fSKyle EvansThis list provides a name and short description for variables
1138378665fSKyle Evansthat can be used for source builds.
1148378665fSKyle Evans.Bl -tag -width indent
1158378665fSKyle Evans]]
1168378665fSKyle Evans
1178378665fSKyle Evanslocal output_tail <const> = [[.El
1188378665fSKyle Evans.Sh FILES
1198378665fSKyle Evans.Bl -tag -compact -width Pa
1208378665fSKyle Evans.It Pa /etc/src.conf
1218378665fSKyle Evans.It Pa /etc/src-env.conf
1228378665fSKyle Evans.It Pa /usr/share/mk/bsd.own.mk
1238378665fSKyle Evans.El
1248378665fSKyle Evans.Sh SEE ALSO
1258378665fSKyle Evans.Xr make 1 ,
1268378665fSKyle Evans.Xr make.conf 5 ,
1278378665fSKyle Evans.Xr build 7 ,
1288378665fSKyle Evans.Xr ports 7
1298378665fSKyle Evans.Sh HISTORY
1308378665fSKyle EvansThe
1318378665fSKyle Evans.Nm
1328378665fSKyle Evansfile appeared in
1338378665fSKyle Evans.Fx 7.0 .
1348378665fSKyle Evans.Sh AUTHORS
1358378665fSKyle EvansThis manual page was autogenerated by
1368378665fSKyle Evans.An tools/build/options/makeman .
1378378665fSKyle Evans]]
1388378665fSKyle Evans
1398378665fSKyle Evanslocal scriptdir <const> = libgen.dirname(stdlib.realpath(arg[0]))
1408378665fSKyle Evanslocal srcdir <const> = stdlib.realpath(scriptdir .. "/../../../")
1418378665fSKyle Evanslocal makesysdir <const> = srcdir .. "/share/mk"
1428378665fSKyle Evans
1438378665fSKyle Evanslocal make_envvar = os.getenv("MAKE")
1448378665fSKyle Evanslocal make_cmd_override = {}
1458378665fSKyle Evansif make_envvar then
1468378665fSKyle Evans	for word in make_envvar:gmatch("[^%s]+") do
1478378665fSKyle Evans		make_cmd_override[#make_cmd_override + 1] = word
1488378665fSKyle Evans	end
1498378665fSKyle Evansend
1508378665fSKyle Evans
1518378665fSKyle Evans-- Lifted from bsdinstall/scripts/pkgbase.in (read_all)
1528378665fSKyle Evanslocal function read_pipe(pipe)
1538378665fSKyle Evans	local ret = ""
1548378665fSKyle Evans	repeat
1558378665fSKyle Evans		local buffer = assert(unistd.read(pipe, 1024))
1568378665fSKyle Evans		ret = ret .. buffer
1578378665fSKyle Evans	until buffer == ""
1588378665fSKyle Evans	return ret
1598378665fSKyle Evansend
1608378665fSKyle Evanslocal function run_make(args)
1618378665fSKyle Evans	local cmd_args = {"env", "-i", "make", "-C", srcdir, "-m", makesysdir,
1628378665fSKyle Evans	    "__MAKE_CONF=/dev/null", "SRCCONF=/dev/null"}
1638378665fSKyle Evans
1648378665fSKyle Evans	if #make_cmd_override > 0 then
1658378665fSKyle Evans		cmd_args[3] = make_cmd_override[1]
1668378665fSKyle Evans		for k = 2, #make_cmd_override do
1678378665fSKyle Evans			local val = make_cmd_override[k]
1688378665fSKyle Evans
1698378665fSKyle Evans			table.insert(cmd_args, 3 + (k - 1), val)
1708378665fSKyle Evans		end
1718378665fSKyle Evans	end
1728378665fSKyle Evans	for k, v in ipairs(args) do
1738378665fSKyle Evans		cmd_args[#cmd_args + 1] = v
1748378665fSKyle Evans	end
1758378665fSKyle Evans
1768378665fSKyle Evans	local r, w = assert(unistd.pipe())
1778378665fSKyle Evans	local pid = assert(unistd.fork())
1788378665fSKyle Evans	if pid == 0 then
1798378665fSKyle Evans		-- Child
1808378665fSKyle Evans		assert(unistd.close(r))
1818378665fSKyle Evans		assert(unistd.dup2(w, 1))
1828378665fSKyle Evans		assert(unistd.dup2(w, 2))
1838378665fSKyle Evans		assert(unistd.execp("env", cmd_args))
1848378665fSKyle Evans		unistd._exit()
1858378665fSKyle Evans	end
1868378665fSKyle Evans
1878378665fSKyle Evans	-- Parent
1888378665fSKyle Evans	assert(unistd.close(w))
1898378665fSKyle Evans
1908378665fSKyle Evans	local output = read_pipe(r)
1918378665fSKyle Evans	assert(unistd.close(r))
1928378665fSKyle Evans
1938378665fSKyle Evans	local _, exit_type, exit_code = assert(sys_wait.wait(pid))
1948378665fSKyle Evans	assert(exit_type == "exited", "make exited with wrong status")
1958378665fSKyle Evans	assert(exit_code == 0, "make exited unsuccessfully")
1968378665fSKyle Evans	return output
1978378665fSKyle Evansend
1988378665fSKyle Evans
1998378665fSKyle Evanslocal function native_target()
2008378665fSKyle Evans	local output = run_make({"MK_AUTO_OBJ=NO", "-V", "MACHINE",
2018378665fSKyle Evans	    "-V", "MACHINE_ARCH"})
2028378665fSKyle Evans
2038378665fSKyle Evans	local arch, machine_arch
2048378665fSKyle Evans	for x in output:gmatch("[^\n]+") do
2058378665fSKyle Evans		if not arch then
2068378665fSKyle Evans			arch = x
2078378665fSKyle Evans		elseif not machine_arch then
2088378665fSKyle Evans			machine_arch = x
2098378665fSKyle Evans		end
2108378665fSKyle Evans	end
2118378665fSKyle Evans
2128378665fSKyle Evans	return arch .. "/" .. machine_arch
2138378665fSKyle Evansend
2148378665fSKyle Evans
2158378665fSKyle Evanslocal function src_targets()
2168378665fSKyle Evans	local targets = {}
2178378665fSKyle Evans	targets[native_target()] = true
2188378665fSKyle Evans
2198378665fSKyle Evans	local output = run_make({"MK_AUTO_OBJ=no", "targets"})
2208378665fSKyle Evans	local curline = 0
2218378665fSKyle Evans
2228378665fSKyle Evans	for line in output:gmatch("[^\n]+") do
2238378665fSKyle Evans		curline = curline + 1
2248378665fSKyle Evans		if curline ~= 1 then
2258378665fSKyle Evans			local arch = line:match("[^%s]+/[^%s]+")
2268378665fSKyle Evans
2278378665fSKyle Evans			-- Make sure we don't roll over our default arch
2288378665fSKyle Evans			if arch and not targets[arch] then
2298378665fSKyle Evans				targets[arch] = false
2308378665fSKyle Evans			end
2318378665fSKyle Evans		end
2328378665fSKyle Evans	end
2338378665fSKyle Evans
2348378665fSKyle Evans	return targets
2358378665fSKyle Evansend
2368378665fSKyle Evans
2378378665fSKyle Evanslocal function config_options(srcconf, env, take_dupes, linting)
2388378665fSKyle Evans	srcconf = srcconf or "/dev/null"
2398378665fSKyle Evans	env = env or {}
2408378665fSKyle Evans
2418378665fSKyle Evans	local option_args = {".MAKE.MODE=normal", "showconfig",
2428378665fSKyle Evans	    "SRC_ENV_CONF=" .. srcconf}
2438378665fSKyle Evans
2448378665fSKyle Evans	for _, val in ipairs(env) do
2458378665fSKyle Evans		option_args[#option_args + 1] = val
2468378665fSKyle Evans	end
2478378665fSKyle Evans
2488378665fSKyle Evans	local output = run_make(option_args)
2498378665fSKyle Evans
2508378665fSKyle Evans	local options = {}
2518378665fSKyle Evans	local known_dupes = {}
2528378665fSKyle Evans
2538378665fSKyle Evans	local function warn_on_dupe(option, val)
2548378665fSKyle Evans		if not linting or known_dupes[option] then
2558378665fSKyle Evans			return false
2568378665fSKyle Evans		end
2578378665fSKyle Evans		if not option:match("^OPT_") then
2588378665fSKyle Evans			val = val == "yes"
2598378665fSKyle Evans		end
2608378665fSKyle Evans
2618378665fSKyle Evans		known_dupes[option] = true
2628378665fSKyle Evans		return val ~= options[val]
2638378665fSKyle Evans	end
2648378665fSKyle Evans
2658378665fSKyle Evans	for opt in output:gmatch("[^\n]+") do
2668378665fSKyle Evans		if opt:match("^MK_[%a%d_]+%s+=%s+.+") then
2678378665fSKyle Evans			local name = opt:match("MK_[%a%d_]+")
2688378665fSKyle Evans			local val = opt:match("= .+"):sub(3)
2698378665fSKyle Evans
2708378665fSKyle Evans			-- Some settings, e.g., MK_INIT_ALL_ZERO, may end up
2718378665fSKyle Evans			-- output twice for some reason that I haven't dug into;
2728378665fSKyle Evans			-- take the first value.  In some circumstances, though,
2738378665fSKyle Evans			-- we do make an exception and actually want to take the
2748378665fSKyle Evans			-- latest.
2758378665fSKyle Evans			if take_dupes or options[name] == nil then
2768378665fSKyle Evans				options[name] = val == "yes"
2778378665fSKyle Evans			elseif warn_on_dupe(name, val) then
2788378665fSKyle Evans				io.stderr:write("ignoring duplicate option " ..
2798378665fSKyle Evans				    name .. "\n")
2808378665fSKyle Evans			end
2818378665fSKyle Evans		elseif opt:match("^OPT_[%a%d_]+%s+=%s+.+") then
2828378665fSKyle Evans			local name = opt:match("OPT_[%a%d_]+")
2838378665fSKyle Evans			local val = opt:match("= .+"):sub(3)
2848378665fSKyle Evans
2858378665fSKyle Evans			-- Multi-value options will arbitrarily use a table here
2868378665fSKyle Evans			-- to indicate the difference.
2878378665fSKyle Evans			if take_dupes or options[name] == nil then
2888378665fSKyle Evans				options[name] = val
2898378665fSKyle Evans			elseif warn_on_dupe(name, val) then
2908378665fSKyle Evans				io.stderr:write("ignoring duplicate option " ..
2918378665fSKyle Evans				    name .. "\n")
2928378665fSKyle Evans			end
2938378665fSKyle Evans		end
2948378665fSKyle Evans	end
2958378665fSKyle Evans
2968378665fSKyle Evans	return options
2978378665fSKyle Evansend
2988378665fSKyle Evans
2998378665fSKyle Evanslocal function env_only_options()
3008378665fSKyle Evans	local output = run_make({"MK_AUTO_OBJ=no", "-V", "__ENV_ONLY_OPTIONS"})
3018378665fSKyle Evans	local options = {}
3028378665fSKyle Evans
3038378665fSKyle Evans	for opt in output:gmatch("[^%s]+") do
3048378665fSKyle Evans		options["MK_" .. opt] = true
3058378665fSKyle Evans	end
3068378665fSKyle Evans
3078378665fSKyle Evans	return options
3088378665fSKyle Evansend
3098378665fSKyle Evans
3108378665fSKyle Evanslocal function required_options()
3118378665fSKyle Evans	local output = run_make({"-f", "share/mk/src.opts.mk", "-V",
3128378665fSKyle Evans	    "__REQUIRED_OPTIONS"})
3138378665fSKyle Evans	local options = {}
3148378665fSKyle Evans
3158378665fSKyle Evans	for opt in output:gmatch("[^%s]+") do
3168378665fSKyle Evans		options["MK_" .. opt] = true
3178378665fSKyle Evans	end
3188378665fSKyle Evans
3198378665fSKyle Evans	return options
3208378665fSKyle Evansend
3218378665fSKyle Evans
3228378665fSKyle Evanslocal function config_description(option_name)
3238378665fSKyle Evans	local fh = io.open(scriptdir .. "/" .. option_name)
3248378665fSKyle Evans	local desc
3258378665fSKyle Evans
3268378665fSKyle Evans	if fh then
3278378665fSKyle Evans		desc = ""
3288378665fSKyle Evans		for line in fh:lines() do
3298378665fSKyle Evans			if not line:match("%$FreeBSD%$") then
3308378665fSKyle Evans				desc = desc .. line .. "\n"
3318378665fSKyle Evans			end
3328378665fSKyle Evans		end
3338378665fSKyle Evans
3348378665fSKyle Evans		assert(fh:close())
3358378665fSKyle Evans	end
3368378665fSKyle Evans
3378378665fSKyle Evans	return desc
3388378665fSKyle Evansend
3398378665fSKyle Evans
3408378665fSKyle Evanslocal function config_descriptions(options)
3418378665fSKyle Evans	local desc = {}
3428378665fSKyle Evans	for name, _ in pairs(options) do
3438378665fSKyle Evans		if name:match("^MK_") then
3448378665fSKyle Evans			local basename = name:gsub("^MK_", "")
3458378665fSKyle Evans			local with_name = "WITH_" .. basename
3468378665fSKyle Evans			local without_name = "WITHOUT_" .. basename
3478378665fSKyle Evans
3488378665fSKyle Evans			desc[with_name] = config_description(with_name)
3498378665fSKyle Evans			desc[without_name] = config_description(without_name)
3508378665fSKyle Evans		elseif name:match("^OPT_") then
3518378665fSKyle Evans			local basename = name:gsub("^OPT_", "")
3528378665fSKyle Evans
3538378665fSKyle Evans			desc[name] = config_description(basename)
3548378665fSKyle Evans		end
3558378665fSKyle Evans	end
3568378665fSKyle Evans	return desc
3578378665fSKyle Evansend
3588378665fSKyle Evans
3598378665fSKyle Evanslocal function dependent_options(tmpdir, option_name, all_opts, omit_others)
3608378665fSKyle Evans	local opt_sense = not not option_name:match("^WITH_")
3618378665fSKyle Evans	local base_option_name = option_name:gsub("^[^_]+_", "")
3628378665fSKyle Evans	local prefix = (opt_sense and "WITHOUT_") or "WITH_"
3638378665fSKyle Evans
3648378665fSKyle Evans	local srcconf = tmpdir .. "/src-" ..prefix .. "ALL_" ..
3658378665fSKyle Evans	    option_name .. ".conf"
3668378665fSKyle Evans	local fh = assert(io.open(srcconf, "w+"))
3678378665fSKyle Evans
3688378665fSKyle Evans	fh:write(option_name .. "=\"YES\"\n")
3698378665fSKyle Evans	if not omit_others then
3708378665fSKyle Evans		for opt, value in pairs(all_opts) do
3718378665fSKyle Evans			local base_opt = opt:gsub("^MK_", "")
3728378665fSKyle Evans
3738378665fSKyle Evans			if base_opt ~= base_option_name then
3748378665fSKyle Evans				local opt_prefix = (value and "WITH_") or "WITHOUT_"
3758378665fSKyle Evans				fh:write(opt_prefix .. base_opt .. "=\"YES\"\n")
3768378665fSKyle Evans			end
3778378665fSKyle Evans		end
3788378665fSKyle Evans	end
3798378665fSKyle Evans	assert(fh:close())
3808378665fSKyle Evans
3818378665fSKyle Evans	local option_name_key = "MK_" .. base_option_name
3828378665fSKyle Evans	local options = config_options(srcconf, nil, omit_others)
3838378665fSKyle Evans	for name, value in pairs(options) do
3848378665fSKyle Evans		if name == option_name_key or value == all_opts[name] then
3858378665fSKyle Evans			options[name] = nil
3868378665fSKyle Evans		elseif name:match("^OPT_") then
3878378665fSKyle Evans			-- Strip out multi-option values at the moment, they do
3888378665fSKyle Evans			-- not really make sense.
3898378665fSKyle Evans			options[name] = nil
3908378665fSKyle Evans		end
3918378665fSKyle Evans	end
3928378665fSKyle Evans
3938378665fSKyle Evans	return options
3948378665fSKyle Evansend
3958378665fSKyle Evans
3968378665fSKyle Evanslocal function export_option_table(fd, name, options)
3978378665fSKyle Evans	unistd.write(fd, name .. " = {")
3988378665fSKyle Evans	for k, v in pairs(options) do
3998378665fSKyle Evans		v = (v and "true") or "false"
4008378665fSKyle Evans		unistd.write(fd, "['" .. k .. "'] = " .. v .. ",")
4018378665fSKyle Evans	end
4028378665fSKyle Evans	unistd.write(fd, "}")
4038378665fSKyle Evansend
4048378665fSKyle Evans
4058378665fSKyle Evanslocal function all_dependent_options(tmpdir, options, default_opts,
4068378665fSKyle Evans    with_all_opts, without_all_opts)
4078378665fSKyle Evans	local all_enforced_options = {}
4088378665fSKyle Evans	local all_effect_options = {}
4098378665fSKyle Evans	local children = {}
4108378665fSKyle Evans
4118378665fSKyle Evans	for _, name in ipairs(options) do
4128378665fSKyle Evans		local rfd, wfd = assert(unistd.pipe())
4138378665fSKyle Evans		local pid = assert(unistd.fork())
4148378665fSKyle Evans
4158378665fSKyle Evans		if pid == 0 then
4168378665fSKyle Evans			-- We need to pcall() this so that errors bubble up to
4178378665fSKyle Evans			-- our _exit() call rather than the main exit.
4188378665fSKyle Evans			local ret, errobj = pcall(function()
4198378665fSKyle Evans				unistd.close(rfd)
4208378665fSKyle Evans
4218378665fSKyle Evans				local compare_table
4228378665fSKyle Evans				if name:match("^WITHOUT") then
4238378665fSKyle Evans					compare_table = with_all_opts
4248378665fSKyle Evans				else
4258378665fSKyle Evans					compare_table = without_all_opts
4268378665fSKyle Evans				end
4278378665fSKyle Evans
4288378665fSKyle Evans				-- List of knobs forced on by this one
4298378665fSKyle Evans				local enforced_options = dependent_options(tmpdir, name,
4308378665fSKyle Evans				    compare_table)
4318378665fSKyle Evans				-- List of knobs implied by this by one (once additionally
4328378665fSKyle Evans				-- filtered based on enforced_options values)
4338378665fSKyle Evans				local effect_options = dependent_options(tmpdir, name,
4348378665fSKyle Evans				    default_opts, true)
4358378665fSKyle Evans
4368378665fSKyle Evans				export_option_table(wfd, "enforced_options",
4378378665fSKyle Evans				    enforced_options)
4388378665fSKyle Evans				export_option_table(wfd, "effect_options",
4398378665fSKyle Evans				    effect_options)
4408378665fSKyle Evans			end)
4418378665fSKyle Evans
4428378665fSKyle Evans			io.stderr:write(".")
4438378665fSKyle Evans
4448378665fSKyle Evans			if ret then
4458378665fSKyle Evans				unistd._exit(0)
4468378665fSKyle Evans			else
4478378665fSKyle Evans				unistd.write(wfd, errobj)
4488378665fSKyle Evans				unistd._exit(1)
4498378665fSKyle Evans			end
4508378665fSKyle Evans		end
4518378665fSKyle Evans
4528378665fSKyle Evans		unistd.close(wfd)
4538378665fSKyle Evans		children[pid] = {name, rfd}
4548378665fSKyle Evans	end
4558378665fSKyle Evans
4568378665fSKyle Evans	while next(children) ~= nil do
4578378665fSKyle Evans::again::
4588378665fSKyle Evans		local pid, status, exitcode = sys_wait.wait(-1)
4598378665fSKyle Evans
4608378665fSKyle Evans		if status ~= "exited" then
4618378665fSKyle Evans			goto again
4628378665fSKyle Evans		end
4638378665fSKyle Evans
4648378665fSKyle Evans		local info = children[pid]
4658378665fSKyle Evans		children[pid] = nil
4668378665fSKyle Evans
4678378665fSKyle Evans		local name = info[1]
4688378665fSKyle Evans		local rfd = info[2]
4698378665fSKyle Evans		local buf = ''
4708378665fSKyle Evans		local rbuf, sz
4718378665fSKyle Evans
4728378665fSKyle Evans		-- Drain the pipe
4738378665fSKyle Evans		rbuf = unistd.read(rfd, 512)
4748378665fSKyle Evans		while #rbuf ~= 0 do
4758378665fSKyle Evans			buf = buf .. rbuf
4768378665fSKyle Evans			rbuf = unistd.read(rfd, 512)
4778378665fSKyle Evans		end
4788378665fSKyle Evans
4798378665fSKyle Evans		unistd.close(rfd)
4808378665fSKyle Evans
4818378665fSKyle Evans		if exitcode ~= 0 then
4828378665fSKyle Evans			error("Child " .. pid .. " failed, buf: " .. buf)
4838378665fSKyle Evans		end
4848378665fSKyle Evans
4858378665fSKyle Evans		-- The child has written a pair of tables named enforced_options
4868378665fSKyle Evans		-- and effect_options to the pipe.  We'll load the pipe buffer
4878378665fSKyle Evans		-- as a string and then yank these out of the clean environment
4888378665fSKyle Evans		-- that we execute the chunk in.
4898378665fSKyle Evans		local child_env = {}
4908378665fSKyle Evans		local res, err = pcall(load(buf, "child", "t", child_env))
4918378665fSKyle Evans
4928378665fSKyle Evans		all_enforced_options[name] = child_env["enforced_options"]
4938378665fSKyle Evans		all_effect_options[name] = child_env["effect_options"]
4948378665fSKyle Evans	end
4958378665fSKyle Evans
4968378665fSKyle Evans	io.stderr:write("\n")
4978378665fSKyle Evans	return all_enforced_options, all_effect_options
4988378665fSKyle Evansend
4998378665fSKyle Evans
5008378665fSKyle Evanslocal function get_defaults(target_archs, native_default_opts)
5018378665fSKyle Evans	local target_defaults = {}
5028378665fSKyle Evans	-- Set of options with differing defaults in some archs
5038378665fSKyle Evans	local different_defaults = {}
5048378665fSKyle Evans
5058378665fSKyle Evans	for tgt, dflt in pairs(target_archs) do
5068378665fSKyle Evans		if dflt then
5078378665fSKyle Evans			local native_copy = {}
5088378665fSKyle Evans			for opt, val in pairs(native_default_opts) do
5098378665fSKyle Evans				native_copy[opt] = val
5108378665fSKyle Evans			end
5118378665fSKyle Evans			target_defaults[tgt] = native_copy
5128378665fSKyle Evans			goto skip
5138378665fSKyle Evans		end
5148378665fSKyle Evans
5158378665fSKyle Evans		local target = tgt:gsub("/.+$", "")
5168378665fSKyle Evans		local target_arch = tgt:gsub("^.+/", "")
5178378665fSKyle Evans
5188378665fSKyle Evans		local target_opts = config_options(nil, {"TARGET=" .. target,
5198378665fSKyle Evans		    "TARGET_ARCH=" .. target_arch})
5208378665fSKyle Evans
5218378665fSKyle Evans		for opt, val in pairs(target_opts) do
5228378665fSKyle Evans			if val ~= native_default_opts[opt] then
5238378665fSKyle Evans				different_defaults[opt] = true
5248378665fSKyle Evans			end
5258378665fSKyle Evans		end
5268378665fSKyle Evans
5278378665fSKyle Evans		target_defaults[tgt] = target_opts
5288378665fSKyle Evans::skip::
5298378665fSKyle Evans	end
5308378665fSKyle Evans
5318378665fSKyle Evans	for opt in pairs(native_default_opts) do
5328378665fSKyle Evans		if different_defaults[opt] == nil then
5338378665fSKyle Evans			for _, opts in pairs(target_defaults) do
5348378665fSKyle Evans				opts[opt] = nil
5358378665fSKyle Evans			end
5368378665fSKyle Evans		end
5378378665fSKyle Evans	end
5388378665fSKyle Evans
5398378665fSKyle Evans	for tgt, opts in pairs(target_defaults) do
5408378665fSKyle Evans		local val = opts["MK_ACPI"]
5418378665fSKyle Evans
5428378665fSKyle Evans		if val ~= nil then
5438378665fSKyle Evans			print(" - " .. tgt .. ": " .. ((val and "yes") or "no"))
5448378665fSKyle Evans		end
5458378665fSKyle Evans	end
5468378665fSKyle Evans
5478378665fSKyle Evans	return target_defaults, different_defaults
5488378665fSKyle Evansend
5498378665fSKyle Evans
5508378665fSKyle Evanslocal function option_comparator(lhs, rhs)
5518378665fSKyle Evans	-- Convert both options to the base name, compare that instead unless
5528378665fSKyle Evans	-- they're the same option.  For the same option, we just want to get
5538378665fSKyle Evans	-- ordering between WITH_/WITHOUT_ correct.
5548378665fSKyle Evans	local base_lhs = lhs:gsub("^[^_]+_", "")
5558378665fSKyle Evans	local base_rhs = rhs:gsub("^[^_]+_", "")
5568378665fSKyle Evans
5578378665fSKyle Evans	if base_lhs == base_rhs then
5588378665fSKyle Evans		return lhs < rhs
5598378665fSKyle Evans	else
5608378665fSKyle Evans		return base_lhs < base_rhs
5618378665fSKyle Evans	end
5628378665fSKyle Evansend
5638378665fSKyle Evans
5648378665fSKyle Evanslocal function main(tmpdir)
5658378665fSKyle Evans	io.stderr:write("building src.conf.5 man page from files in " ..
5668378665fSKyle Evans	    scriptdir .. "\n")
5678378665fSKyle Evans
5688378665fSKyle Evans	local env_only_opts <const> = env_only_options()
5698378665fSKyle Evans	local default_opts = config_options(nil, nil, nil, true)
5708378665fSKyle Evans	local opt_descriptions = config_descriptions(default_opts)
5718378665fSKyle Evans	local srcconf_all <const> = tmpdir .. "/src-all-enabled.conf"
5728378665fSKyle Evans	local fh = io.open(srcconf_all, "w+")
5738378665fSKyle Evans	local all_targets = src_targets()
5748378665fSKyle Evans	local target_defaults, different_defaults = get_defaults(all_targets,
5758378665fSKyle Evans	    default_opts)
5768378665fSKyle Evans	local options = {}
5778378665fSKyle Evans	local without_all_opts = {}
5788378665fSKyle Evans
5798378665fSKyle Evans	for name, value in pairs(default_opts) do
5808378665fSKyle Evans		if name:match("^MK_") then
5818378665fSKyle Evans			local base_name = name:gsub("^MK_", "")
5828378665fSKyle Evans			local with_name = "WITH_" .. base_name
5838378665fSKyle Evans			local without_name = "WITHOUT_" .. base_name
5848378665fSKyle Evans			-- If it's differently defaulted on some architectures,
5858378665fSKyle Evans			-- we'll split it into WITH_/WITHOUT_ just to simplify
5868378665fSKyle Evans			-- some later bits.
5878378665fSKyle Evans			if different_defaults[name] ~= nil then
5888378665fSKyle Evans				options[#options + 1] = with_name
5898378665fSKyle Evans				options[#options + 1] = without_name
5908378665fSKyle Evans			elseif value then
5918378665fSKyle Evans				options[#options + 1] = without_name
5928378665fSKyle Evans			else
5938378665fSKyle Evans				options[#options + 1] = with_name
5948378665fSKyle Evans			end
5958378665fSKyle Evans
5968378665fSKyle Evans			without_all_opts[name] = false
5978378665fSKyle Evans			assert(fh:write(with_name .. '="YES"\n'))
5988378665fSKyle Evans		else
5998378665fSKyle Evans			options[#options + 1] = name
6008378665fSKyle Evans		end
6018378665fSKyle Evans	end
6028378665fSKyle Evans
6038378665fSKyle Evans	assert(fh:close())
6048378665fSKyle Evans
6058378665fSKyle Evans	local with_all_opts = config_options(srcconf_all)
6068378665fSKyle Evans	local all_enforced_options, all_effect_options
6078378665fSKyle Evans	local all_required_options = required_options()
6088378665fSKyle Evans
6098378665fSKyle Evans	all_enforced_options, all_effect_options = all_dependent_options(tmpdir,
6108378665fSKyle Evans	    options, default_opts, with_all_opts, without_all_opts)
6118378665fSKyle Evans
6128378665fSKyle Evans	table.sort(options, option_comparator)
6138378665fSKyle Evans	io.stdout:write(output_head)
6148378665fSKyle Evans	for _, name in ipairs(options) do
6158378665fSKyle Evans		local value
6168378665fSKyle Evans
6178378665fSKyle Evans		if name:match("^OPT_") then
6188378665fSKyle Evans			goto skip
6198378665fSKyle Evans		end
6208378665fSKyle Evans		assert(name:match("^WITH"), "Name looks wrong: " .. name)
6218378665fSKyle Evans		local describe_option = name
6228378665fSKyle Evans
6238378665fSKyle Evans		value = not not name:match("^WITHOUT")
6248378665fSKyle Evans
6258378665fSKyle Evans		-- Normalize name to MK_ for indexing into various other
6268378665fSKyle Evans		-- arrays
6278378665fSKyle Evans		name = "MK_" .. name:gsub("^[^_]+_", "")
6288378665fSKyle Evans
6298378665fSKyle Evans		print(".It Va " .. describe_option:gsub("^OPT_", ""))
6308378665fSKyle Evans		if opt_descriptions[describe_option] then
6318378665fSKyle Evans			io.stdout:write(opt_descriptions[describe_option])
6328378665fSKyle Evans		else
6338378665fSKyle Evans			io.stderr:write("Missing description for " ..
6348378665fSKyle Evans			    describe_option .. "\n")
6358378665fSKyle Evans		end
6368378665fSKyle Evans
6378378665fSKyle Evans		local enforced_options = all_enforced_options[describe_option]
6388378665fSKyle Evans		local effect_options = all_effect_options[describe_option]
6398378665fSKyle Evans
6408378665fSKyle Evans		if different_defaults[name] ~= nil then
6418378665fSKyle Evans			print([[.Pp
6428378665fSKyle EvansThis is a default setting on]])
6438378665fSKyle Evans
6448378665fSKyle Evans			local which_targets = {}
6458378665fSKyle Evans			for tgt, tgt_options in pairs(target_defaults) do
6468378665fSKyle Evans				if tgt_options[name] ~= value then
6478378665fSKyle Evans					which_targets[#which_targets + 1] = tgt
6488378665fSKyle Evans				end
6498378665fSKyle Evans			end
6508378665fSKyle Evans
6518378665fSKyle Evans			table.sort(which_targets)
6528378665fSKyle Evans			for idx, tgt in ipairs(which_targets) do
6538378665fSKyle Evans				io.stdout:write(tgt)
6548378665fSKyle Evans				if idx < #which_targets - 1 then
6558378665fSKyle Evans					io.stdout:write(", ")
6568378665fSKyle Evans				elseif idx == #which_targets - 1 then
6578378665fSKyle Evans					io.stdout:write(" and ")
6588378665fSKyle Evans				end
6598378665fSKyle Evans			end
6608378665fSKyle Evans			print(".")
6618378665fSKyle Evans		end
6628378665fSKyle Evans
6638378665fSKyle Evans		-- Unset any implied options that are actually required.
6648378665fSKyle Evans		for dep_opt in pairs(enforced_options) do
6658378665fSKyle Evans			if all_required_options[dep_opt] then
6668378665fSKyle Evans				enforced_options[dep_opt] = nil
6678378665fSKyle Evans			end
6688378665fSKyle Evans		end
6698378665fSKyle Evans		if next(enforced_options) ~= nil then
6708378665fSKyle Evans			print([[When set, it enforces these options:
6718378665fSKyle Evans.Pp
6728378665fSKyle Evans.Bl -item -compact]])
6738378665fSKyle Evans
6748378665fSKyle Evans			local sorted_dep_opt = {}
6758378665fSKyle Evans			for dep_opt in pairs(enforced_options) do
6768378665fSKyle Evans				sorted_dep_opt[#sorted_dep_opt + 1] = dep_opt
6778378665fSKyle Evans			end
6788378665fSKyle Evans
6798378665fSKyle Evans			table.sort(sorted_dep_opt)
6808378665fSKyle Evans			for _, dep_opt in ipairs(sorted_dep_opt) do
6818378665fSKyle Evans				local dep_val = enforced_options[dep_opt]
6828378665fSKyle Evans				local dep_prefix = (dep_val and "WITH_") or
6838378665fSKyle Evans				    "WITHOUT_"
6848378665fSKyle Evans				local dep_name = dep_opt:gsub("^MK_",
6858378665fSKyle Evans				    dep_prefix)
6868378665fSKyle Evans				print(".It")
6878378665fSKyle Evans				print(".Va " .. dep_name)
6888378665fSKyle Evans			end
6898378665fSKyle Evans
6908378665fSKyle Evans			print(".El")
6918378665fSKyle Evans		end
6928378665fSKyle Evans
6938378665fSKyle Evans		if next(effect_options) ~= nil then
6948378665fSKyle Evans			if next(enforced_options) ~= nil then
6958378665fSKyle Evans				-- Remove any options that were previously
6968378665fSKyle Evans				-- noted as enforced...
6978378665fSKyle Evans				for opt, val in pairs(effect_options) do
6988378665fSKyle Evans					if enforced_options[opt] == val then
6998378665fSKyle Evans						effect_options[opt] = nil
7008378665fSKyle Evans					end
7018378665fSKyle Evans				end
7028378665fSKyle Evans
7038378665fSKyle Evans				-- ... and this could leave us with an empty
7048378665fSKyle Evans				-- set.
7058378665fSKyle Evans				if next(effect_options) == nil then
7068378665fSKyle Evans					goto noenforce
7078378665fSKyle Evans				end
7088378665fSKyle Evans
7098378665fSKyle Evans				print(".Pp")
7108378665fSKyle Evans			end
7118378665fSKyle Evans
7128378665fSKyle Evans			print([[When set, these options are also in effect:
7138378665fSKyle Evans.Pp
7148378665fSKyle Evans.Bl -inset -compact]])
7158378665fSKyle Evans
7168378665fSKyle Evans			local sorted_dep_opt = {}
7178378665fSKyle Evans			for dep_opt in pairs(effect_options) do
7188378665fSKyle Evans				sorted_dep_opt[#sorted_dep_opt + 1] = dep_opt
7198378665fSKyle Evans			end
7208378665fSKyle Evans
7218378665fSKyle Evans			table.sort(sorted_dep_opt)
7228378665fSKyle Evans			for _, dep_opt in ipairs(sorted_dep_opt) do
7238378665fSKyle Evans				local dep_val = effect_options[dep_opt]
7248378665fSKyle Evans				local dep_prefix = (dep_val and "WITH_") or
7258378665fSKyle Evans				    "WITHOUT_"
7268378665fSKyle Evans				local not_dep_prefix = ((not dep_val) and "WITH_") or
7278378665fSKyle Evans				    "WITHOUT_"
7288378665fSKyle Evans				local dep_name = dep_opt:gsub("^MK_",
7298378665fSKyle Evans				    dep_prefix)
7308378665fSKyle Evans				local not_dep_name = dep_opt:gsub("^MK_",
7318378665fSKyle Evans				    not_dep_prefix)
7328378665fSKyle Evans
7338378665fSKyle Evans				print(".It Va " .. dep_name)
7348378665fSKyle Evans				print("(unless")
7358378665fSKyle Evans				print(".Va " .. not_dep_name)
7368378665fSKyle Evans				print("is set explicitly)")
7378378665fSKyle Evans			end
7388378665fSKyle Evans
7398378665fSKyle Evans			print(".El")
7408378665fSKyle Evans::noenforce::
7418378665fSKyle Evans		end
7428378665fSKyle Evans
7438378665fSKyle Evans		if env_only_opts[name] ~= nil then
7448378665fSKyle Evans			print([[.Pp
7458378665fSKyle EvansThis must be set in the environment, make command line, or
7468378665fSKyle Evans.Pa /etc/src-env.conf ,
7478378665fSKyle Evansnot
7488378665fSKyle Evans.Pa /etc/src.conf .]])
7498378665fSKyle Evans		end
7508378665fSKyle Evans		::skip::
7518378665fSKyle Evans	end
7528378665fSKyle Evans	print([[.El
7538378665fSKyle Evans.Pp
7548378665fSKyle EvansThe following options accept a single value from a list of valid values.
7558378665fSKyle Evans.Bl -tag -width indent]])
7568378665fSKyle Evans	for _, name in ipairs(options) do
7578378665fSKyle Evans		if name:match("^OPT_") then
7588378665fSKyle Evans			local desc = opt_descriptions[name]
7598378665fSKyle Evans
7608378665fSKyle Evans			print(".It Va " .. name:gsub("^OPT_", ""))
7618378665fSKyle Evans			if desc then
7628378665fSKyle Evans				io.stdout:write(desc)
7638378665fSKyle Evans			else
7648378665fSKyle Evans				io.stderr:write("Missing description for " ..
7658378665fSKyle Evans				    name .. "\n")
7668378665fSKyle Evans			end
7678378665fSKyle Evans		end
7688378665fSKyle Evans	end
7698378665fSKyle Evans	io.stdout:write(output_tail)
7708378665fSKyle Evansend
7718378665fSKyle Evans
7728378665fSKyle Evanslocal tmpdir = "/tmp/makeman." .. unistd.getpid()
7738378665fSKyle Evans
7748378665fSKyle Evansif not lfs.mkdir(tmpdir) then
7758378665fSKyle Evans	error("Failed to create tempdir " .. tmpdir)
7768378665fSKyle Evansend
7778378665fSKyle Evans
7788378665fSKyle Evans-- Catch any errors so that we can properly clean up, then re-throw it.
7798378665fSKyle Evanslocal ret, errobj = pcall(main, tmpdir)
7808378665fSKyle Evans
7818378665fSKyle Evansfor fname in lfs.dir(tmpdir) do
7828378665fSKyle Evans	if fname ~= "." and fname ~= ".." then
7838378665fSKyle Evans		assert(os.remove(tmpdir .. "/" .. fname))
7848378665fSKyle Evans	end
7858378665fSKyle Evansend
7868378665fSKyle Evans
7878378665fSKyle Evansif not lfs.rmdir(tmpdir) then
7888378665fSKyle Evans	assert(io.stderr:write("Failed to clean up tmpdir: " .. tmpdir .. "\n"))
7898378665fSKyle Evansend
7908378665fSKyle Evans
7918378665fSKyle Evansif not ret then
7928378665fSKyle Evans	io.stderr:write(errobj .. "\n")
7938378665fSKyle Evans	os.exit(1)
7948378665fSKyle Evansend
795