From Fedora Project Wiki

< PackagingDrafts‎ | ScriptletSnippets

Revision as of 06:46, 22 April 2009 by Toshio (talk | contribs) (fix formatting)

Warning.png
This page is a draft only
It is still under construction and content may change. Do not rely on the information on this page.

Changelog

0.1: First go at this

Existing Guideline

GConf XML schema registration

Currently, when a package requires an XML schema it is installed in %{_sysconfdir}/gconf/schemas/ (such as gcalctool in the following example), gconftool-2 should be run after the package is installed/uninstalled in FC4 or later. If the package is upgraded, the existing schema is deleted and removed from the XML schema tree (registry) and then the package provided schema is installed and registered. This is required so that the program(s) have a default configuration and for any custom options a user may wish to edit with using gconf-editor.

Note that this is required for any package that uses GConf for its configuration that must register its configurations into the registry.


%find_lang gcalctool --with-gnome

%post
scrollkeeper-update -q
export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
gconftool-2 --makefile-install-rule %{_sysconfdir}/gconf/schemas/gcalctool.schemas >  /dev/null || :

%pre
if [ "$1" -gt 1 ]; then
  export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
  gconftool-2 --makefile-uninstall-rule %{_sysconfdir}/gconf/schemas/gcalctool.schemas > /dev/null || :
fi

%preun
if [ "$1" -eq 0 ]; then
  export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
  gconftool-2 --makefile-uninstall-rule %{_sysconfdir}/gconf/schemas/gcalctool.schemas > /dev/null || :
fi

Proposal

Motivations for changing the existing guideline:

  • Speed up package install/upgrade. Right now it is painful when preforming Anaconda installs and yum updates for new packages, it takes a long time for the XML schemas to register and this is done for each GConf related package and each sub-package as well.
  • gconftool-2 currently runs on every package install/upgrade, which is very inefficient. When preforming an upgrade of a package using the existing guidelines, there is no comparing of the existing schema and this needlessly removes and re-registers with an unchanged schema, slowing down Anaconda installs and yum updates.

New GConf registration

Using OpenSuSE's existing macros.gconf2 with minor modifications (as needed) we can dramatically improve install and upgrade times for our users. Upgrading existing RPMs will have their schemas compared and if they are the same, no changes are done to the user's system configuration registry. If there are changes, then the schema is registered as normal within %posttrans section.

Since schema registrations are usually not dependant on other packages being installed (and probably should not be) we are able to push these registrations to a %posttrans section.

Typical example of how the new macros work:

Name: gcalctool
...
...
...
Requires(post): GConf2
...
%gconf_schemas_prereq  [This basically is just Requires(pre): GConf2 coreutils diffutils ]

%install 
...
# Find GConf XML Schemas
%find_gconf_schemas

%find_lang gcalctool --with-gnome
cat %{name}.schemas_list %{name}.lang >%{name}.lst

%pre -f %{name}.schemas_pre

%preun -f %{name}.schemas_preun

%posttrans -f %{name}.schemas_posttrans

#%files -f gcalctool.lang
%files -f %{names}.lst
#%{_sysconfdir}/gconf/schemas/gcalctool.schemas


  • rpm-scriptlets are generated by the macros in %pre, %preun, %posttrans
  • We do not need to explicitly include the schema files in the %file section.

Description of new GConf macros

The following macros are available:

gconf_schemas_prereq - This macro just looks for some pre-requirements.
find_gconf_schemas - Searches the package during build time in $RPM_BUILD_ROOT/etc/gconf/schemas and builds a list of them and creates rpm-scriptlets in the process for each schema file found.
def_gconf_schemas - Define a schema, set file permissions for use with %file section, create %pre, %preun, %posttrans rpm-scriptlets.
add_gconf_schemas - Creates %pre section setup up comparing existing schema with new one, creates uninstall scriptlet for %preun section to remove schema from registry.
end_gconf_schemas - You must call this macro if you do not use the find_gconf_schemas macro. This macro does a compare list against schemas that need to be registered in a %posttrans scriptlet and which ones need to be uninstalled in a %preun scriptlet.

The Macro file macro.gconf2 (original)

  • License from author is LGPL v2.1+ from email conversation:

"I did not assign any special license to macros.gconf2.

I think that if not declared otherwise, rpm source files have the same license as the package itself. In case of gconf2 it is: GPL v2 or later; LGPL v2.1 or later

So feel free to consider it as LGPL v2.1 or later.

I think, that LGPL should allow to use them in proprietary GNOME packages, exactly as gconf2 license allows.

Maybe it would be good to mention the license explicitly."

- Stanislav Brabec

#                                                                           
# RPM macros for gconf applications.                                        
#                                                                           
# (c) 2006-2007 SUSE LINUX                                                  
# Author: Stanislav Brabec <sbrabec@suse.cz>                                
#                                                                           
# Usage:                                                                    
#                                                                           
# Preamble:                                                                 
# %gconf_schemas_prereq                                                     
#                                                                           
# %install                                                                  
# ...                                                                       
# %find_gconf_schemas                                                       
# or                                                                        
# %def_gconf_schemas subpackage1                                            
# %add_gconf_schemas schemas_name1                                          
# %add_gconf_schemas schemas_name2                                          
# %def_gconf_schemas subpackage2                                            
# %add_gconf_schemas schemas_name3                                          
# %add_gconf_schemas schemas_name4                                          
# %end_gconf_schemas                                                        
# schemas names are without path and suffix                                 
#                                                                           
# %pre -f %{name}.schemas_pre                                               
#                                                                           
# %preun -f %{name}.schemas_preun                                           
#                                                                           
# %posttrans -f %{name}.schemas_posttrans                                   
#                                                                           
# %files -f %{name}.schemas_list                                            
# or                                                                        
# %files -f subpackage.schemas_list                                         
#                                                                           
#                                                                           
# In case of more -f needed for one %files:                                 
#                                                                           
# %install                                                                  
# ...                                                                       
# %find_gconf_schemas                                                       
# %find_lang %{name}                                                        
# cat %{name}.schemas_list %{name}.lang >%{name}.lst                        
#                                                                           
# %files -f %{name}.lst                                                     
#                                                                           

%gconf_schemas_prereq PreReq: /usr/bin/gconftool-2 coreutils diffutils

%end_gconf_schemas() \
echo 'if test "x$UNINSTALL_SCHEMA_FILES" != "x"; then\
    usr/bin/gconftool-2 --makefile-uninstall-rule $UNINSTALL_SCHEMA_FILES >/dev/null\
    rm -f $UNINSTALL_SCHEMA_FILES\                                                   
fi\                                                                                  
if test "x$INSTALL_SCHEMA_FILES" != "x"; then\                                       
    usr/bin/gconftool-2 --makefile-install-rule $INSTALL_SCHEMA_FILES >/dev/null\    
fi\                                                                                  
rmdir etc/gconf/schemas/outdated 2>/dev/null || true' >>$GCONF_SCHEMAS_NAME.schemas_posttrans\
echo '    rmdir etc/gconf/schemas/outdated 2>/dev/null || true\                               
fi' >>$GCONF_SCHEMAS_NAME.schemas_preun\                                                      
%{nil}                                                                                        

%def_gconf_schemas() \
if test -n "$GCONF_SCHEMAS_NAME" ; then\
    %end_gconf_schemas\                 
fi\                                     
GCONF_SCHEMAS_NAME=%1\                  
echo '%defattr (644, root, root, 755)' >%1.schemas_list\
echo 'mkdir -p etc/gconf/schemas/outdated' >%1.schemas_pre\
echo 'export GCONF_CONFIG_SOURCE=`usr/bin/gconftool-2 --get-default-source`\
INSTALL_SCHEMA_FILES=\                                                      
UNINSTALL_SCHEMA_FILES=' >%1.schemas_posttrans\                             
echo 'if test "$1" == "0"; then\                                            
    if test -x usr/bin/gconftool-2 ; then\                                  
        export GCONF_CONFIG_SOURCE=`usr/bin/gconftool-2 --get-default-source`\
    fi' >%1.schemas_preun\                                                    
%{nil}                                                                        

%_add_gconf_schemas() \
echo $SCHEMAS >>$GCONF_SCHEMAS_NAME.schemas_list\
echo "if test -f ${SCHEMAS#/} ; then\            
    ln -f ${SCHEMAS#/} etc/gconf/schemas/outdated/\
fi" >>$GCONF_SCHEMAS_NAME.schemas_pre\             
echo "if test -f etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/}; then\
    cmp --quiet etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/} ${SCHEMAS#/}\
    if test \\\$? != 0; then\                                                          
        UNINSTALL_SCHEMA_FILES=\\\"\\\$UNINSTALL_SCHEMA_FILES etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/}\\\"\                                                                            
        INSTALL_SCHEMA_FILES=\\\"\\\$INSTALL_SCHEMA_FILES ${SCHEMAS#/}\\\"\                         
    else\                                                                                           
        rm -f etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/}\
    fi\
else\
  INSTALL_SCHEMA_FILES=\\\"\\\$INSTALL_SCHEMA_FILES ${SCHEMAS#/}\\\"\
fi" >>$GCONF_SCHEMAS_NAME.schemas_posttrans\
echo "    if test -x usr/bin/gconftool-2 ; then\
        if test -f etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/} ; then\
            usr/bin/gconftool-2 --makefile-uninstall-rule etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/} >/dev/null\
        elif test -f ${SCHEMAS#/} ; then\
            usr/bin/gconftool-2 --makefile-uninstall-rule ${SCHEMAS#/} >/dev/null\
        fi\
    fi\
    rm -f etc/gconf/schemas/outdated/${SCHEMAS#/etc/gconf/schemas/}" >>$GCONF_SCHEMAS_NAME.schemas_preun\
%{nil}

%add_gconf_schemas() \
SCHEMAS=/etc/gconf/schemas/%1.schemas\
%_add_gconf_schemas\
%{nil}

%find_gconf_schemas() \
test -d $RPM_BUILD_ROOT/etc/gconf/schemas\
%def_gconf_schemas %{name}\
for SCHEMAS in $RPM_BUILD_ROOT/etc/gconf/schemas/*.schemas ; do\
    test -f $SCHEMAS\
    SCHEMAS=${SCHEMAS#$RPM_BUILD_ROOT}\
    %_add_gconf_schemas\
done\
%end_gconf_schemas\
%{nil}


Discussion

This is a bit complex and adds several new things. I'd like to break up the changes and make sure that what we end up is both faster and as simple as possible. Here's the changes I see:

1. Use %posttrans 2. generate scriptlets in macros 3. Generate Requires() in macros 4. Compare gconf schemas and don't run gconftool if no changes were made

Let's take these one at a time

%posttrans

I don't think this adds any speed as we still have to run gconftool-2 the same number of times over the same number of new schemas. The window for things to go wonky is larger if we wait until %posttrans to run the gconf update instead of running it in %post. It's possible that a gconf using app would be needed later in the transaction, although the fact that it needed the schema installed to function correctly is technically a bug.

Generate scriptlets in macros

I like the idea of getting complexity out of people's sight but I recently helped package a font and discovered there's costs to using macros. It really obfuscates what the code is doing.

Also, when macros change, packages that make use of them have to be rebuilt to pick up the changes. We should figure out how to track what's been rebuilt and what hasn't to make this better.

Update: This will do for finding the schema including packages:

repoquery --whatprovides "/etc/gconf/schemas/*" |sort |uniq |wc -l

Note: One case that this doesn't catch is packages that used to provide schemas but no longer do.

Generate Requires in macros

I'd rather do something like this:

Requires(pre): %{gconf-schema-post-requires}

But I could be persuaded that it's better to hide it all.

Note that the current macro isn't complete. You need to have separate entries for each of %pre, %preun, and %posttrans.

Also note, coreutils and diffutils are in our buildsys-build package so the Prereq is really just the equivalent of Requires(pre): GConf2 which doesn't add that much value.

Compare gconf schemas

This looks like the big speed win. If we could reduce the complexity and just do this that would be a good thing. We'd want to know what the costs are of enhancing performance though. How much more complex? Does the window for interrupted rpm transactions get bigger or the results of that get worse?

I've never written an rpm macro before, but would this work? It's simpler:

#
# RPM macros for gconf applications.
#
# (c) 2006-2007 SUSE LINUX
# Author: Stanislav Brabec <sbrabec@suse.cz>
#
# Usage:
#
# Preamble:
# Requires(pre): GConf2
# Requires(preun): GConf2
# Requires(post): GConf2
# Requires(postun): GConf2
#
# schemas names are without path and suffix
#
#
# %pre
# %{gconf_schema_obsolete: schema1}
# %{gconf_schema_prepare: schema2 schema3}
#
# %preun
# %{gconf_schema_remove: schema2 schema3}
#
# %post
# %{gconf_schema_upgrade: schema2 schema3}
#
# %files
# %{_sysconfdir}/gconf/schemas/schema1.schemas
# %{_sysconfdir}/gconf/schemas/schema2.schemas

# Copy schemas from old package for later uninstall on upgrade
%gconf_schema_prepare(schemas) \
    if [ "$1" -gt 1 ] ; then \
        rm -rf %{_localstatedir}/lib/rpm-state/gconf/* \
        for schema in %{schemas} ; do \
            cp -p "$schema" %{_localstatedir}/lib/rpm-state/gconf/ \
        done \
    fi \
%{nil}

# Macro to remove schemas.  Not meant to be used publically
%_do_gconf_schema_remove(schema) \
    export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source` \
    gconftool-2 --makefile-uninstall-rule %{schema} > /dev/null || : \
%{nil}

# Remove schemas unconditionally
%gconf_schema_obsolete(schemas) \
    for schema in %{schemas} ; do \
        %_do_gconf_schema_remove(%{_sysconfdir}/gconf/schemas/"$schema".schemas) \
    done \
%{nil}

# Remove schemas on package removal (not upgrade)
%gconf_schema_remove(schemas) \
    if [ "$1" -eq 0 ] ; then \
        for schema in %{schemas} ; do \
            %_do_gconf_schema_remove(%{_sysconfdir}/gconf/schemas/"$schema".schemas) \
        done \
    fi \
%{nil}

%gconf_schema_upgrade(schemas) \
    for schema in %{schemas} ; do \
        if test -f %{_localstatedir}/lib/rpm-state/gconf/"$schema".schemas ; then \
            cmp --quiet %{_sysconfdir}/gconf/schemas/"$schema".schemas %{_localstatedir}/lib/rpm-state/gconf/"$schema".schemas \
        if test \\\$? != 0; then\
            %_do_gconf_schema_remove(%{_localstatedir}/lib/rpm-state/gconf/"$schema".schemas) \
            export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source` \
            gconftool-2 --makefile-install-rule %{_sysconfdir}/gconf/schemas/"$schema".schemas > /dev/null || : \
        else \
            rm -f %{_localstatedir}/lib/rpm-state/gconf/"$schema".schemas \
        fi\
    done \
%{nil}

In this example, schema1 and schema2 were installed by older versions of the package. At some point the package started installing schema2 and schema3 instead. We still have to anticipate upgrading from a package that had schema1 installed.

Notes:

  • Need to find a package to own /var/lib/rpm-state/gconf. GConf2, rpm, or filesystem could work.