[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Packages: A first Draft
As a first step towards a simpler and more powerful module management,
I want to formalize the concept of "packages". Typically, a package
contains a library, plus some programs or scripts. In the oo2c
environment, a package usually comes as a tar file. Ultimately,
installation of a new package on a target system should be achieved by
running a single command.
Prospective packages are, for example, the OOC Core library, Tim's
VisualOberon, the XML, and the ADT library.
Attached is the first cut of a document type definition of the package
manifest. At the end of the mail an example manifest is given for the
XML library.
As always, comments are welcome.
-- mva
########################################################################
file: OOC-Repository.dtd
########################################################################
<?xml version="1.0" encoding="UTF-8" ?> <!-- document version: 0.1 -->
<!-- If you are a member of the bottom-up fraction, you should read
this text from start to end. If you are a top-down person, you will
be more comfortable reading it the other way. -->
<!-- Every library provides a version number for the interface it
implements. Here, the term "library" denotes a set of modules that
work closely together and are distributed as a single unit. A version
number is a simple mechanism to keep track of changes in the library's
interface. While version numbers are not used by the Oberon compiler
itself, they are necessary to check dependences between packages, and
to create and maintain shared libraries (or dynamic link libraries) on
the target operating system.
OOC uses the version numbering system of the GNU libtool package. To
quote the libtool documentation (version 1.2): "Libtool has its own
formal versioning system. It is not as flexible as some, but it is
definitely the simplest of the more powerful versioning systems." A
libtool version string has the format "CURRENT[:REVISION[:AGE]]". If
either REVISION or AGE are omitted, they default to 0. AGE must be
less than or equal to the CURRENT interface number.
Every library's version is described by three integers:
CURRENT
The most recent interface number that this library implements.
REVISION
The implementation number of the CURRENT interface.
AGE
The difference between the newest and oldest interfaces that this
library implements. In other words, the library implements all the
interface numbers in the range from number `CURRENT - AGE' to
`CURRENT'.
An example: the version "3:12:1" denotes the current version "3",
revision "12", and the interface has an age of "1". A library with
such a version implements both the interfaces "3" and "2". It would
supersede all libraries with interface "2", and all libraries "3:n:1"
with a revision `n' less than 12.
Note that for a particular target operating system, libtool version
strings are usually mapped to the target specific version numbering.
A library's version number should only be adjusted for a public
release. In general, it does not correspond to some software version
number, or a version identifier generated by a revision control
system. -->
<!ELEMENT interface_version EMPTY>
<!ATTLIST interface_version current CDATA #REQUIRED
revision CDATA "0"
age CDATA "0">
<!-- A program version is not as formally defined as an interface
version. In its simplest form, it is just an integer, representing
the program's major version number. In its most excessive form, it is
a 4-tuple of integer numbers. I suggest the following approach to
program version numbers:
1) The major version number is rarely changed, only after severe
changes to the program.
2) The minor number changes with significant changes in a release.
Usually, it signals a new feature or an important change in some
part of the program.
3) The revision number is increased if a public release does not
warrant a change in the minor or major version number.
4) The patchlevel number is never used for public releases. If it
is given as part of the version string, it signals a intermediate
beta version, that is intended to be tested by selected people.
Usually, such releases are only made available as patches against
the latest version with a patchlevel of zero.
-->
<!ELEMENT program_version EMPTY>
<!ATTLIST program_version major CDATA #REQUIRED
minor CDATA "0"
revision CDATA "0"
patchlevel CDATA "0">
<!-- Every entity (module, library, program, script) should have a
short description. The description should be a short, say, 2-5 lines,
statement summarizing the purpose of the entity. -->
<!ELEMENT description (#PCDATA)>
<!-- Every module is written by someone. In time, other people modify
and enhance the program code, thus becoming co-authors. An author is
identified by her and email address. The name should not include any
titles or punctions characters. Example:
</author name="Michael van Acken" email="acken@informatik.uni-kl.de">
-->
<!ELEMENT author EMPTY>
<!ATTLIST author name CDATA #REQUIRED
email CDATA #IMPLIED>
<!-- The maintainer is the current caretaker of the module. Any
questions or bug reports should be directed to the maintainer, if her
name is given. Otherwise, it is assumed that the author is also the
maintainer. -->
<!ELEMENT maintainer EMPTY>
<!ATTLIST maintainer name CDATA #REQUIRED
email CDATA #IMPLIED>
<!-- Every piece of code published by its author is distributed under
some license. With a license, the author defines under which terms
and conditions other people may use, copy, modify, or distribute her
work. IMPORTANT: Every source file must include a license statement!
The license tags in a package manifest are no replacement for these
statements. If the author chooses a non-standard license of her own
making, the license tag should read "Custom-made". A library
comprising modules under different licenses should summarize its own
license as "Mixed". Because this complicates matters greatly for the
library user, this case should be avoided at all costs. Also note,
that sometimes a piece of software may be distributed under several
licenses at once.
Licenses are defined by a enumeration. Every identifier should
represent a single license definition. The list of licenses will grow
over time, although personally I would prefer if any new modules are
published under the GPL.
GPL GNU General Public License
LGPL GNU Lesser General Public License
PD source text is in the public domain
Custom-made Non-standard license defined by author
Mixed Only applicable to libraries, deprecated
-->
<!ELEMENT license EMPTY>
<!ATTLIST license name NOTATION
(GPL |
LGPL |
PD |
Custom-made |
Mixed)
#REQUIRED>
<!-- A "foreign_source" element holds the file name of a piece of
program text (source code or object code) that is necessary to compile
an Oberon-2 module. The file name must not have a directory part.
The file resides in the same directory as the module source text it
belongs to. -->
<!ELEMENT foreign_source EMPTY>
<!ATTLIST foreign_source file_name CDATA #REQUIRED>
<!-- A module element describes an Oberon-2 module. The description
is usually just the module name, without any file name suffix. For
modules that rely on foreign code (like assembler, C, or even object
files), the description also includes the file names of the foreign
source files. -->
<!ELEMENT module (maintainer?,
author+,
license+,
description?,
foreign_source*)>
<!ATTLIST module name CDATA #REQUIRED>
<!-- A library is a set of modules. Typically, the modules are
closely related, are distributed as a single unit, and are used by
several programs. If a module is the atomic unit of reusability, a
library should be the logical unit of code reuse. Every library has a
unique name, matching the regular expression
[a-zA-Z][a-zA-Z0-9_]*
The library name is used to identify the library, e.g. when referring
to it in dependence statements, and to create static or shared library
objects from it, e.g. for OOC systems running under Unix or Win32.
Under oo2c, e.g. running under Linux, a shared library named "foo"
will be installed as the system library "libooc_foo". The prefix
"ooc_" is inserted to avoid name clashed with libraries from other
sources. -->
<!ELEMENT library (interface_version,
description?,
module+)>
<!ATTLIST library name CDATA #REQUIRED>
<!-- A program is created by running a "make" command on a designated
main module. The created executable is installed as part of the
package. The modules that are used by programs, but are not part of
a library, are not copied over during installation. Unlike libraries,
programs have no version number of their own. When installed, they
can use the package's version number. -->
<!ELEMENT program EMPTY>
<!ATTLIST program main_module CDATA #REQUIRED>
<!ELEMENT programs (program+,
description?,
module*)>
<!-- A script is a non-Oberon file, executable by some interpreter on
the target operating system. It is installed as is, without any
modification. Use of this element type is deprecated, but I may need
this for the oo2c compiler distribution. The file name is relative to
the scripts subdirectory of a package. -->
<!ELEMENT script (description?)>
<!ATTLIST script file_name CDATA #REQUIRED>
<!-- Sometimes a package includes non-executable content, that is
nevertheless necessary to use the library, or the program. Examples
for this are VO's icons, the compiler's error lists, or the Emacs Lisp
files installed with oo2c. All these files are listed in the package
description, and are copied into the package's resource directory
during installation. The file name is relative to the resources
subdirectory of a package. -->
<!ELEMENT resource EMPTY>
<!ATTLIST resource file_name CDATA #REQUIRED>
<!-- To function properly, a library usually depends on other
libraries. For example, even the most basic module is usually a
client of one or several modules of the OOC core library. If a
package is being installed, and it depends on a library that is either
not present, or has an potentially incompatible interface, the error
should be reported by the installation tool.
The required version number is given in a shortened format
"INTERFACE:REVISION", meaning that the package expects to get a
library implementing INTERFACE, with the designated REVISION or later.
Note that a dependency does not quote the package on which it depends,
but rather the library name. This way it is possible to have multiple
packages providing the same library. An example for this: VO uses a
hardware abstraction library, that is dependent on the target
platform. There exists an implementation for X11, and another for
Win32. The different implementations would have different package
names, but provide the same library name. -->
<!ELEMENT depends_on_library EMPTY>
<!ATTLIST depends_on_library name CDATA #REQUIRED
interface CDATA #REQUIRED
revision CDATA "0">
<!-- If a package can optionally make use of other libraries, but for
some reason does not want to make this a strict requirement, it can
list the libraries in "suggests_library" elements. For such a
suggested library "foo", a boolean pragma variable "HAVE_LIBRARY_foo"
is declared. If the library is installed, its value is TRUE,
otherwise it is FALSE. Installation of the package fails, if it
suggests a library whose interface version does not meet the version
requirements. -->
<!ELEMENT suggests_library EMPTY>
<!ATTLIST suggests_library name CDATA #REQUIRED
interface CDATA #REQUIRED
revision CDATA "0">
<!-- The old configuration variables from the section OPTIONS and
PRAGMAS are defined by option_variable and pragma_variable elements.
It is an error if any of the variables listed here is already defined
by another package or is a predefined variable of the compiler.
Option and pragma variables use different name spaces. It is
suggested that the variable names are prefixed by the library or
package name, followed by an underscore. -->
<!ELEMENT option_variable (#PCDATA)>
<!ATTLIST option_variable type NOTATION
(BOOLEAN |
INTEGER |
STRING) #REQUIRED>
<!ELEMENT pragma_variable (#PCDATA)>
<!ATTLIST pragma_variable type NOTATION
(BOOLEAN |
INTEGER |
STRING) #REQUIRED>
<!-- A package is a unit of distribution. It contains at most one
library, and, optionally, a number of programs. It is expected that
the purpose of most packages will be to distribute a set of library
modules. For this reason, a package's version number is that of its
library, unless stated otherwise. It is an error if the package has
no library, nor a version number. Unless taken from the library
definition, the package's version attribute has no formal definition.
It is suggested that in this case the format follows the established
program_version 3-tuple (or 4-tuple) convention. -->
<!ELEMENT package (depends_on_library*,
library?,
programs?,
script*,
resource_file*,
option_variable*,
pragma_variable*)>
<!ATTLIST package version CDATA #IMPLIED>
<!--
Notes:
0) This document covers the data that must be present in a package.
It does not define how packages are installed into repositories, nor
which data is stored in a repository.
1) If source code is signed, e.g. using PGP, it is done on the package
level. The package, typically a tar+gzip archive with oo2c, is the
unit of disitribution, and any client should be able to check if the
package has been tampered with. A finer grained control, e.g. through
signitures on the module level, offers little benefits.
2) The amount of license information in a package manifest may seem
excessive. Because a module is an atomic piece of work, I believe it
is important to have a concise overview over the licenses layed down
by the author(s) of the modules that make up a library, or a program.
3) Only dependences upon libraries are modeled, and even this
mechanism is restricted to Oberon-2 libraries that were installed
using the package mechanism. This is intentionally. The Oberon
design is that of an extensible system, where the building blocks are
modules. The concept of programs does not fit into this, and
therefore I believe that there is little to model dependences upon
programs. This also simplifies the design of the package and
repository system greatly, because they must only keep track of
modules and libraries, and can ignore programs altogether.
4) Keeping a package description and a source tree in sync can be very
troublesome. For example, simply checking if all source files are
listed in the XML document, and, vice versa, if all listed files do
exist in reality, is a tedious and error prone task. For such things
there will be tool so that they can be automated.
-->
########################################################################
file: package.xml
########################################################################
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE package SYSTEM "OOC-Repository.dtd">
<package> <!-- package version is taken from the library definition -->
<depends_on_library name="core" interface_version="9"/>
<library name="XML">
<interface_version current="0" revision="0" age="0"/>
<module name="XML:Codec">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Codec:ASCII">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Codec:Latin1">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Codec:UTF16">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Codec:UTF8">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Config">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Msg">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:MsgBoard">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:ErrBoard">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:EntityResolver">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:SymbolTable">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Stream">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Scanner">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Parser">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Locator">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Builder">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Builder:CanonicalUTF8">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
<module name="XML:Builder:StructListing">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
</library>
<programs>
<program main_module="XMLTest"/>
<module name="XMLTest">
<author name="Michael van Acken" email="acken@informatik.uni-kl.de"/>
<license name="GPL"/>
</module>
</programs>
</package>