[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] Re-enable grub-extras
From: |
Colin Watson |
Subject: |
Re: [PATCH] Re-enable grub-extras |
Date: |
Thu, 23 Sep 2010 14:08:20 +0100 |
User-agent: |
Mutt/1.5.18 (2008-05-17) |
On Tue, Sep 21, 2010 at 07:35:30PM +0100, Colin Watson wrote:
> Here's a patch to re-enable grub-extras by allowing extra modules to
> provide Autogen definitions files. A few things to note:
Vladimir commented on this on IRC, noting three problems. In reverse
order of complexity:
1) The GRUB_CONTRIB check in autogen.sh should also check for
grub-core/contrib.
Fixed.
2) What's the problem with renaming g2hdr.bin to g2hdr.img?
I know of at least one piece of software which expects the current file
name: win32-loader, written by Robert and maintained in Debian. It
probably wouldn't be completely awful to change this, but it would be
nice not to need to. That said, perhaps g2hdr.bin could be a symlink to
g2hdr.img (which could be done in Makefile.common). I've updated my
patch to do this, and removed the extension-override facility from
gentpl.py.
3) It would be nice if extra modules could add new sources to existing
targets defined in one of the main definitions files: for example, it
would be useful to be able to add zfs to libgrub.a.
This was the hard bit that took me a day or two to fix.
Vladimir's original suggestion on IRC was to create a separate
libgrubzfs.a and arrange to link the utilities with that as well. When
I looked into this, I decided that I didn't like this approach because
it simply pushes the problem of appending to an existing list up a
level; as far as the Autogen definitions go, there isn't a significant
difference between appending to grub_probe_LDADD and appending to
libgrub_a_SOURCES. I would prefer to solve this some other way.
My preferred approach is to try not to expose any of the difficulties in
the definitions file, so that it can just look like this:
library = {
name = libgrub.a;
common = contrib/zfs/zfs.c;
common = contrib/zfs/zfs_lzjb.c;
common = contrib/zfs/zfs_sha256.c;
common = contrib/zfs/zfs_fletcher.c;
cppflags = '$(ZFS_CPPFLAGS)';
cflags = '$(ZFS_CFLAGS)';
};
Doing this actually turned out to be very difficult because Autogen's
native macros aren't powerful enough: they can only really emit text
rather than using temporary storage, which means that you can't do
things like "look for all the library blocks with the same name and
concatenate them" or "append to _SOURCES variables if we've seen them
before". Some Automake variables can be handled with simple appends
while some of them have some boilerplate only at the start which
shouldn't be repeated; Automake variables must be initialised exactly
once on all possible intersections of conditional paths; and we should
also avoid generating duplicate rules. All these constraints make it a
hard problem.
Fortunately, Autogen supports embedding bits of Guile, which makes the
problem merely hard rather than impossible. I tried a few different
approaches, and the one that I found to be most practical is in the
following patch: effectively we remember whether we've seen a given
target before and treat it slightly differently, and furthermore we emit
initialisations for non-global variables to avoid problems with
Automake's restrictions on variable initialisation.
For now, I only implemented this for 'library' blocks, so something that
wants to compile extra files into the GRUB kernel for example will be
out of luck. If people feel this is an onerous restriction then it's
fairly easy to fix; I was just trying not to change too much at once,
particularly for paths I wasn't going to test.
Doing this involved feeding all the files relevant to a given Automake
input stream to Autogen in one go, so I eliminated Makefile.gcry.am.
This was an exercise in programming in six languages at once (Python,
Autogen native macros, Guile, Autogen definitions, Automake, and make).
I found it to be a bit much, and my impression is that Autogen got in
the way more than it helped. I suggest that after 1.99 we should look
at having a Python program generate Automake input directly, which would
avoid many contortions and allow us to improve the output (it could
merge the multiple _SOURCES lines into just one per conditional path,
for instance). The .def file format is of course just fine as it is,
and could easily be preserved.
> * Extra modules may also provide Makefile.common with literal Automake
> input, for those cases where it's too hard to cram things into
> Autogen.
I also added Makefile.core.common and Makefile.util.common (starting to
run out of good names) for literal Automake input that should only be
used in grub-core or at the top level respectively. ntldr-img needed
the former.
Once again, the required patches to grub-extras modules are attached.
2010-09-22 Colin Watson <address@hidden>
Re-enable grub-extras.
* autogen.sh: Create symlinks to ${GRUB_CONTRIB} if necessary to
avoid confusing Automake. Run autogen only twice, once for the top
level and once for grub-core. Add Makefile.util.def and
Makefile.core.def from extra modules to the appropriate autogen
invocations. If Makefile.common exists in an extra module, include
it in both Makefile.util.am and grub-core/Makefile.core.am;
similarly, include any Makefile.util.common file in Makefile.util.am
and any Makefile.core.common file in grub-core/Makefile.core.am.
* conf/Makefile.common ($(top_srcdir)/grub-core/Makefile.core.am):
Depend on $(top_srcdir)/grub-core/Makefile.gcry.def.
($(top_srcdir)/grub-core/Makefile.gcry.def): Remove.
* grub-core/Makefile.am: Remove inclusion of Makefile.gcry.am.
* gentpl.py (gvar_add): Turn GVARS into a set.
(global_variable_initializers): Sort global variables on output.
(vars_init): New function.
(first_time): Likewise.
(library): Ensure that non-global variable initialisations are
emitted before the first time we emit code for a library block.
Append to variables rather than setting them. Only emit
noinst_LIBRARIES, BUILT_SOURCES, and CLEANFILES the first time for
each conditional path.
(program): installdir() emits an Autogen macro, so must be passed to
var_add rather than gvar_add.
(data): Likewise.
(script): Likewise.
(rules): New function, centralising handling for different target
types. Set up Guile association lists for first_time and vars_init,
and send most output to a diversion so that variable initialisations
can be emitted first.
(module_rules): Use new rules function.
(kernel_rules): Likewise.
(image_rules): Likewise.
(library_rules): Likewise.
(program_rules): Likewise.
(script_rules): Likewise.
(data_rules): Likewise.
* configure.ac: Add AC_PROG_LN_S, for the benefit of ntldr-img.
* .bzrignore: Add contrib and grub-core/contrib. Remove
grub-core/Makefile.gcry.am.
=== modified file '.bzrignore'
--- .bzrignore 2010-09-20 13:03:47 +0000
+++ .bzrignore 2010-09-22 13:07:15 +0000
@@ -104,9 +104,10 @@ grub-core/lib/libgcrypt-grub
**/.deps-core
**/.dirstamp
Makefile.util.am
+contrib
grub-core/Makefile.core.am
-grub-core/Makefile.gcry.am
grub-core/Makefile.gcry.def
+grub-core/contrib
grub-core/genmod.sh
grub-core/gensyminfo.sh
grub-core/*.module
=== modified file 'autogen.sh'
--- autogen.sh 2010-08-23 08:37:29 +0000
+++ autogen.sh 2010-09-23 10:36:52 +0000
@@ -14,9 +14,50 @@ echo "Creating Makefile.tpl..."
python gentpl.py | sed -e '/^$/{N;/^\n$/D;}' > Makefile.tpl
echo "Running autogen..."
-autogen -T Makefile.tpl Makefile.util.def | sed -e '/^$/{N;/^\n$/D;}' >
Makefile.util.am
-autogen -T Makefile.tpl grub-core/Makefile.core.def | sed -e
'/^$/{N;/^\n$/D;}' > grub-core/Makefile.core.am
-autogen -T Makefile.tpl grub-core/Makefile.gcry.def | sed -e
'/^$/{N;/^\n$/D;}' > grub-core/Makefile.gcry.am
+
+# Automake doesn't like including files from a path outside the project.
+rm -f contrib grub-core/contrib
+if [ "x${GRUB_CONTRIB}" != x ]; then
+ [ "${GRUB_CONTRIB}" = contrib ] || ln -s "${GRUB_CONTRIB}" contrib
+ [ "${GRUB_CONTRIB}" = grub-core/contrib ] || ln -s ../contrib
grub-core/contrib
+fi
+
+UTIL_DEFS=Makefile.util.def
+CORE_DEFS='grub-core/Makefile.core.def grub-core/Makefile.gcry.def'
+
+for extra in contrib/*/Makefile.util.def; do
+ if test -e "$extra"; then
+ UTIL_DEFS="$UTIL_DEFS $extra"
+ fi
+done
+
+for extra in contrib/*/Makefile.core.def; do
+ if test -e "$extra"; then
+ CORE_DEFS="$CORE_DEFS $extra"
+ fi
+done
+
+cat $UTIL_DEFS | autogen -T Makefile.tpl | sed -e '/^$/{N;/^\n$/D;}' >
Makefile.util.am
+cat $CORE_DEFS | autogen -T Makefile.tpl | sed -e '/^$/{N;/^\n$/D;}' >
grub-core/Makefile.core.am
+
+for extra in contrib/*/Makefile.common; do
+ if test -e "$extra"; then
+ echo "include $extra" >> Makefile.util.am
+ echo "include $extra" >> grub-core/Makefile.core.am
+ fi
+done
+
+for extra in contrib/*/Makefile.util.common; do
+ if test -e "$extra"; then
+ echo "include $extra" >> Makefile.util.am
+ fi
+done
+
+for extra in contrib/*/Makefile.core.common; do
+ if test -e "$extra"; then
+ echo "include $extra" >> grub-core/Makefile.core.am
+ fi
+done
echo "Saving timestamps..."
echo timestamp > stamp-h.in
=== modified file 'conf/Makefile.common'
--- conf/Makefile.common 2010-09-21 12:37:50 +0000
+++ conf/Makefile.common 2010-09-22 13:09:44 +0000
@@ -146,11 +146,7 @@ $(top_srcdir)/Makefile.util.am: $(top_sr
mv address@hidden $@
.PRECIOUS: $(top_srcdir)/grub-core/Makefile.core.am
-$(top_srcdir)/grub-core/Makefile.core.am:
$(top_srcdir)/grub-core/Makefile.core.def $(top_srcdir)/Makefile.tpl
- autogen -T $(top_srcdir)/Makefile.tpl $< | sed -e '/^$$/{N;/^\\n$$/D;}'
> address@hidden || (rm -f address@hidden; exit 1)
- mv address@hidden $@
-
-.PRECIOUS: $(top_srcdir)/grub-core/Makefile.gcry.am
-$(top_srcdir)/grub-core/Makefile.gcry.am:
$(top_srcdir)/grub-core/Makefile.gcry.def $(top_srcdir)/Makefile.tpl
- autogen -T $(top_srcdir)/Makefile.tpl $< | sed -e '/^$$/{N;/^\\n$$/D;}'
> address@hidden || (rm -f address@hidden; exit 1)
+$(top_srcdir)/grub-core/Makefile.core.am:
$(top_srcdir)/grub-core/Makefile.core.def
$(top_srcdir)/grub-core/Makefile.gcry.def $(top_srcdir)/Makefile.tpl
+ if [ "x$$GRUB_CONTRIB" != x ]; then echo "You need to run ./autogen.sh
manually." >&2; exit 1; fi
+ autogen -T $(top_srcdir)/Makefile.tpl
$(top_srcdir)/grub-core/Makefile.core.def
$(top_srcdir)/grub-core/Makefile.gcry.def | sed -e '/^$$/{N;/^\\n$$/D;}' >
address@hidden || (rm -f address@hidden; exit 1)
mv address@hidden $@
=== modified file 'configure.ac'
--- configure.ac 2010-09-21 09:42:30 +0000
+++ configure.ac 2010-09-22 12:11:35 +0000
@@ -235,6 +235,7 @@ AC_PROG_LEX
AC_PROG_YACC
AC_PROG_MAKE_SET
AC_PROG_MKDIR_P
+AC_PROG_LN_S
if test "x$LEX" = "x:"; then
AC_MSG_ERROR([flex is not found])
=== modified file 'gentpl.py'
--- gentpl.py 2010-09-19 13:24:45 +0000
+++ gentpl.py 2010-09-22 21:01:22 +0000
@@ -70,16 +70,15 @@ for platform in GRUB_PLATFORMS:
#
# Global variables
#
-GVARS = []
+GVARS = set()
def gvar_add(var, value):
- if var not in GVARS:
- GVARS.append(var)
+ GVARS.add(var)
return var + " += " + value + "\n"
def global_variable_initializers():
r = ""
- for var in GVARS:
+ for var in sorted(GVARS):
r += var + " ?= \n"
return r
@@ -87,6 +86,16 @@ def global_variable_initializers():
# Per PROGRAM/SCRIPT variables
#
+def vars_init(*var_list):
+ r = "[+ IF (if (not (assoc-ref seen-vars (get \".name\"))) \"seen\") +]"
+ r += "[+ (out-suspend \"v\") +]"
+ for var in var_list:
+ r += var + " = \n"
+ r += "[+ (out-resume \"v\") +]"
+ r += "[+ (set! seen-vars (assoc-set! seen-vars (get \".name\") 0)) +]"
+ r += "[+ ENDIF +]"
+ return first_time(r)
+
def var_set(var, value):
return var + " = " + value + "\n"
@@ -257,6 +266,15 @@ def platform_ccasflags(p): return platfo
def platform_stripflags(p): return platform_specific_values(p, "_stripflags",
"stripflags")
def platform_objcopyflags(p): return platform_specific_values(p,
"_objcopyflags", "objcopyflags")
+#
+# Emit snippet only the first time through for the current name.
+#
+def first_time(snippet):
+ r = "[+ IF (if (not (assoc-ref seen-target (get \".name\"))) \"seen\") +]"
+ r += snippet
+ r += "[+ ENDIF +]"
+ return r
+
def module(platform):
r = set_canonical_name_suffix(".module")
@@ -341,18 +359,25 @@ fi
def library(platform):
r = set_canonical_name_suffix("")
- r += gvar_add("noinst_LIBRARIES", "[+ name +]")
- r += var_set(cname() + "_SOURCES", platform_sources(platform))
- r += var_set("nodist_" + cname() + "_SOURCES",
platform_nodist_sources(platform))
- r += var_set(cname() + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_LIBRARY) " +
platform_cflags(platform))
- r += var_set(cname() + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_LIBRARY) "
+ platform_cppflags(platform))
- r += var_set(cname() + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_LIBRARY)
" + platform_ccasflags(platform))
- # r += var_set(cname() + "_DEPENDENCIES", platform_dependencies(platform)
+ " " + platform_ldadd(platform))
- r += gvar_add("EXTRA_DIST", extra_dist())
- r += gvar_add("BUILT_SOURCES", "$(nodist_" + cname() + "_SOURCES)")
- r += gvar_add("CLEANFILES", "$(nodist_" + cname() + "_SOURCES)")
+ r += vars_init(cname() + "_SOURCES",
+ "nodist_" + cname() + "_SOURCES",
+ cname() + "_CFLAGS",
+ cname() + "_CPPFLAGS",
+ cname() + "_CCASFLAGS")
+ # cname() + "_DEPENDENCIES")
+ r += first_time(gvar_add("noinst_LIBRARIES", "[+ name +]"))
+ r += var_add(cname() + "_SOURCES", platform_sources(platform))
+ r += var_add("nodist_" + cname() + "_SOURCES",
platform_nodist_sources(platform))
+ r += var_add(cname() + "_CFLAGS", first_time("$(AM_CFLAGS)
$(CFLAGS_LIBRARY) ") + platform_cflags(platform))
+ r += var_add(cname() + "_CPPFLAGS", first_time("$(AM_CPPFLAGS)
$(CPPFLAGS_LIBRARY) ") + platform_cppflags(platform))
+ r += var_add(cname() + "_CCASFLAGS", first_time("$(AM_CCASFLAGS)
$(CCASFLAGS_LIBRARY) ") + platform_ccasflags(platform))
+ # r += var_add(cname() + "_DEPENDENCIES", platform_dependencies(platform)
+ " " + platform_ldadd(platform))
+
+ r += gvar_add("EXTRA_DIST", extra_dist())
+ r += first_time(gvar_add("BUILT_SOURCES", "$(nodist_" + cname() +
"_SOURCES)"))
+ r += first_time(gvar_add("CLEANFILES", "$(nodist_" + cname() +
"_SOURCES)"))
return r
def installdir(default="bin"):
@@ -376,7 +401,7 @@ def program(platform, test=False):
r += gvar_add("check_PROGRAMS", "[+ name +]")
r += gvar_add("TESTS", "[+ name +]")
r += "[+ ELSE +]"
- r += gvar_add(installdir() + "_PROGRAMS", "[+ name +]")
+ r += var_add(installdir() + "_PROGRAMS", "[+ name +]")
r += "[+ IF mansection +]" + manpage() + "[+ ENDIF +]"
r += "[+ ENDIF +]"
@@ -397,7 +422,7 @@ def program(platform, test=False):
def data(platform):
r = gvar_add("EXTRA_DIST", platform_sources(platform))
r += gvar_add("EXTRA_DIST", extra_dist())
- r += gvar_add(installdir() + "_DATA", platform_sources(platform))
+ r += var_add(installdir() + "_DATA", platform_sources(platform))
return r
def script(platform):
@@ -405,7 +430,7 @@ def script(platform):
r += gvar_add("check_SCRIPTS", "[+ name +]")
r += gvar_add ("TESTS", "[+ name +]")
r += "[+ ELSE +]"
- r += gvar_add(installdir() + "_SCRIPTS", "[+ name +]")
+ r += var_add(installdir() + "_SCRIPTS", "[+ name +]")
r += "[+ IF mansection +]" + manpage() + "[+ ENDIF +]"
r += "[+ ENDIF +]"
@@ -418,33 +443,43 @@ chmod a+x [+ name +]
r += gvar_add("dist_noinst_DATA", platform_sources(platform))
return r
+def rules(target, closure):
+ # Create association lists for the benefit of first_time and vars_init.
+ r = "[+ (define seen-target '()) +]"
+ r += "[+ (define seen-vars '()) +]"
+ # Most output goes to a diversion. This allows us to emit variable
+ # initializations before everything else.
+ r += "[+ (out-push-new) +]"
+
+ r += "[+ FOR " + target + " +]"
+ r += foreach_enabled_platform(
+ lambda p: under_platform_specific_conditionals(p, closure(p)))
+ # Remember that we've seen this target.
+ r += "[+ (set! seen-target (assoc-set! seen-target (get \".name\") 0)) +]"
+ r += "[+ ENDFOR +]"
+ r += "[+ (out-pop #t) +]"
+ return r
+
def module_rules():
- return "[+ FOR module +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, module(p))) + "[+
ENDFOR +]"
+ return rules("module", module)
def kernel_rules():
- return "[+ FOR kernel +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, kernel(p))) + "[+
ENDFOR +]"
+ return rules("kernel", kernel)
def image_rules():
- return "[+ FOR image +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, image(p))) + "[+
ENDFOR +]"
+ return rules("image", image)
def library_rules():
- return "[+ FOR library +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, library(p))) + "[+
ENDFOR +]"
+ return rules("library", library)
def program_rules():
- return "[+ FOR program +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, program(p))) + "[+
ENDFOR +]"
+ return rules("program", program)
def script_rules():
- return "[+ FOR script +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, script(p))) + "[+
ENDFOR +]"
+ return rules("script", script)
def data_rules():
- return "[+ FOR data +]" + foreach_enabled_platform(
- lambda p: under_platform_specific_conditionals(p, data(p))) + "[+
ENDFOR +]"
+ return rules("data", data)
print "[+ AutoGen5 template +]\n"
a = module_rules()
=== modified file 'grub-core/Makefile.am'
--- grub-core/Makefile.am 2010-09-19 20:22:43 +0000
+++ grub-core/Makefile.am 2010-09-22 13:11:11 +0000
@@ -51,7 +51,6 @@ grub_script.yy.c: grub_script.yy.h
CLEANFILES += grub_script.yy.c grub_script.yy.h
include $(srcdir)/Makefile.core.am
-include $(srcdir)/Makefile.gcry.am
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h
--
Colin Watson address@hidden