--- /dev/null
+*~
+*.so
+*.lc
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
--- /dev/null
+e2factory 2.3.0pre2
+ * change email addresses
+
+e2factory 2.3.0pre1
+ * create free software package
+ * almost no changes to the source since e2factory 2.2.0
--- /dev/null
+Error handling
+ * Functions should always return:
+ * a boolean value and an error object on failure.
+ * or one or more values, or nil as the first value and an error object on
+ failure.
+ * when an error occurs, an error object must be created and returned
+ * error objects must be handled by the caller by
+ * passing them up
+ * ignoring them (that usually requires a comment)
+ * nesting them into another error object from the current layer
+ * errors shall not be handled by calling e2lib.abort().
+ * calling e2lib.bomb() is allowed when signalling a bug in the tool.
+ E.g. when checking function call parameters, which should never fail.
+ We need the call stack in those cases to fix that bug.
+ * the behaviour if e2lib.bomb() may be different when delivering e2factory
+ to customers. (As they should not see the internals.)
+ * Example:
+ -- the error object from the current layer is usually named 'e'.
+ e = new_error("error copying file for")
+ -- a boolean return value is usually called 'rc'.
+ -- an error object return value is usually called 're'.
+ rc, re = f(...)
+ if not rc then
+ -- nest the error object from the f() call into our own one and return
+ return false, e:cat(re)
+ end
+
+Avoid conditional Code - minimize Code paths
+ * iterate over an empty list instead of checking for nil
+ * apply a default (e.g. emtpy list instead of nil) as early as possible
+ where values are optional when building up data structures
+
+Committing
+ * follow the one-fix-per-commit and one-commit-per-fix policy
+ * that gives us readable commits
+ * do not merge, but rebase, i.e. do not use git-pull to merge your
+ stuff to the upstream branch, but rebase your commits onto the
+ upstream branch.
+ * No merge commits on upstream.
+
--- /dev/null
+INSTALLATION INSTRUCTIONS
+=========================
+
+e2factory, for historical reasons, builds and installs its own lua.
+Make sure you download the lua-5.1.3.tar.gz package and place it in
+the top level of the e2factory source tree prior to building. That
+can be done by creating a symlink.
+
+ ln -s ../lua-5.1.3.tar.gz
+
+To install e2factory, enter the following commands while
+being located in the toplevel source directory (the directory
+where this file can be found):
+
+ make all
+ sudo make install
+
+This will install the global tools in PREFIX/bin, PREFIX/lib, ....
+PREFIX defaults to "/usr/local" can be overridden by passing
+"PREFIX" to the "make" invocations given above:
+
+ make PREFIX=$HOME/my-e2 all
+ sudo make PREFIX=$HOME/my-e2 install
+
+(Note that the same prefix has also to be given in the install
+step)
+
+The installation step HAS to be done as root, as some global tools
+need to have their setuid(1) bit set to create and remove the
+chroot(1) environment.
+
+To build and install the documentation use the doc and install-doc targets:
+
+ make doc
+ make install-doc
+
+This will build and install the tutorial, the html manual pages and the
+luadoc source code documentation.
+
+The e2factory-examples package provides an easy way to get started. It
+holds everything to setup a simple e2factory environment, with a small
+example project.
+We recommend to download and install the e2factory-example package to
+get a working setup. See INSTALL in the e2factory-example package for
+more information.
+
+In case you have any problems with the installation, please
+contact the e2factory team via the mailinglist:
+
+ e2factory-users@lists.e2factory.org
+
+with a description of your problem and the version of e2factory
+that you are using.
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+SUBDIRS = lua generic global doc templates
+LOCALSUBDIRS = lua generic global local doc
+TOPLEVEL = .
+
+include $(TOPLEVEL)/make.vars
+
+CLEAN_FILES = *~ E2_COMMIT
+
+
+.PHONY: all e2commit install install-local clean local localdist uninstall \
+ doc
+
+help:
+ @cat INSTALL
+
+all: e2commit
+ $(MAKE) -C lua
+ $(MAKE) -C generic
+ $(MAKE) -C global
+ $(MAKE) -C doc/man
+ $(MAKE) -C templates all
+
+# this target creates a file E2_COMMIT, holding the current E2_COMMIT
+# string, and cleans the tree in case E2_COMMIT changed since the last
+# time. That makes sure that the builtin version string is always correct.
+
+e2commit:
+ @if [ "$(E2_COMMIT)" != "$(shell cat E2_COMMIT)" ] ; then \
+ echo "E2_COMMIT changed. making clean first." ; \
+ $(MAKE) clean ; \
+ echo "$(E2_COMMIT)" > E2_COMMIT ; \
+ fi
+
+install: all
+ mkdir -p $(DESTDIR)$(BINDIR)
+ mkdir -p $(DESTDIR)$(LIBDIR)
+ mkdir -p $(DESTDIR)$(LIBEXECDIR)
+ mkdir -p $(DESTDIR)$(INCDIR)
+ mkdir -p $(DESTDIR)$(MANDIR)
+ mkdir -p $(DESTDIR)$(TOOLDIR)
+ $(MAKE) -C lua install
+ $(MAKE) -C generic install
+ $(MAKE) -C global install
+ $(MAKE) -C local install
+ $(MAKE) -C doc/man install
+ $(MAKE) -C templates install
+
+uninstall:
+ $(MAKE) -C lua uninstall
+ $(MAKE) -C generic uninstall
+ $(MAKE) -C global uninstall
+ $(MAKE) -C doc/man uninstall
+ $(MAKE) -C templates uninstall
+ $(MAKE) -C local uninstall
+
+local: e2commit
+ $(MAKE) -C generic local
+ $(MAKE) -C local
+ $(MAKE) -C templates local
+
+install-local:
+ scripts/e2-locate-project-root
+ $(MAKE) -C generic install-local
+ $(MAKE) -C local install-local
+ $(MAKE) -C templates install-local
+
+doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc:
+ install -d -m 755 $(DOCDIR)
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+ install -m 644 Changelog $(DOCDIR)/
+
+clean:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+ for s in $(LOCALSUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ; \
+ done
+ rm -f $(CLEAN_FILES)
+ if [ -d $(PWD)/test/e2 ]; then \
+ sudo $(MAKE) -C test clean; \
+ fi
+
+check:
+ @echo building e2...
+ make clean >/dev/null 2>&1
+ make PREFIX=$(PWD)/test/e2 >/dev/null 2>&1
+ sudo make PREFIX=$(PWD)/test/e2 install >/dev/null 2>&1
+ make -C test check
+
+localdist: all
+ if test -z "$(DISTNAME)"; then \
+ echo; \
+ echo "please re-invoke with DISTNAME set to the tag you want to package"; \
+ echo; \
+ exit 1; \
+ fi
+ rm -fr dist
+ $(MAKE) -C local PROJECTDIR=$$PWD/dist install-local
+ $(MAKE) -C generic PROJECTDIR=$$PWD/dist install-local
+ tar -czvf e2-$(DISTNAME)-$(ARCH)-local.tar.gz -C dist .e2
+ rm -fr dist
+
+dist:
+ git archive --format=tar --prefix=$(PACKAGE)/ $(PACKAGE) \
+ >$(PACKAGE).tar
+ gzip <$(PACKAGE).tar >$(PACKAGE).tar.gz
--- /dev/null
+README for e2factory
+====================
+
+
+e2factory is a system for building software in a chroot(1)
+environment. The results of the build are fully reproducible and care
+is taken to avoid re-builds unless dependencies have changed.
+
+See INSTALL for installation instructions.
+
+After installation you can read the manual pages for e2factory,
+possibly starting with
+
+ man e2intro
--- /dev/null
+1. Including the e2 tool version into the buildid calculation
+
+The content of the version file is available in the source in the E2_VERSION
+variable.
+This variable is calculated into the buildid, to catch tool changes and
+be sure to rebuild results whenever the e2 tool version changes.
+
+The E2_COMMIT variable is not used, as that makes development impossible,
+as buildids would change with each single commit.
+
+2. The release process
+
+Releasing e2 requires the following steps:
+step 1. change the ./version file (setting the version string)
+step 2. commit
+step 3. tag
+step 4. change the ./version file (appending the "-wip" postfix again)
+step 5. commit
+step 6. push
+
+3. Version and Release naming
+Any e2 version follows this naming scheme:
+MAJOR.MINOR(.PATCHLEVEL)(preNUMBER|rcNUMBER)?(-wip)?
+The release name is the version name including the "e2-" prefix
+
+4. Shell code to support the release process
+# set the VERSION variable first, e.g. VERSION="2.2pre1-wip"
+RELEASE_NAME="e2-$VERSION"
+echo $VERSION >./version
+vi Changelog # edit the release string
+git commit -m "release $RELEASE_NAME" version Changelog
+git tag "$RELEASE_NAME"
+git push origin "$RELEASE_NAME"
+VERSION=${VERSION}-wip
+RELEASE_NAME="e2-$VERSION"
+echo $VERSION >./version
+vi Changelog # edit the release string: wip again
+git commit -m "work in progress $RELEASE_NAME" version Changelog
+git push
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+SUBDIRS = luadoc man manual
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+CLEAN_FILES = *~ documentation.tar.gz
+
+.PHONY: all e2commit install install-local clean local localdist uninstall \
+ doc
+
+all:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+uninstall:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+local:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-local:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+ $(MAKE) documentation.tar.gz
+
+documentation.tar.gz: luadoc/luadoc.tar.gz \
+ man/man.tar.gz manual/manual.tar.gz
+ rm -fr documentation
+ mkdir documentation
+ tar -C documentation -xzf luadoc/luadoc.tar.gz
+ tar -C documentation -xzf man/man.tar.gz
+ tar -C documentation -xzf manual/manual.tar.gz
+ tar -czf documentation.tar.gz documentation
+
+install-doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+clean:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+ rm -fr documentation
+ rm -f $(CLEAN_FILES)
+
+check:
+ for s in $(SUBDIRS) do ;\
+ $(MAKE) -C $$s $@ ;\
+ done
+
+localdist:
+ for s in $(SUBDIRS) do ;\
+ $(MAKE) -C $$s $@ ;\
+ done
+
--- /dev/null
+1. Dependencies
+1.1 dsort
+print a valid order for building results according to proj/dependencies
+
+1.2 dlist
+read from stdin a result name
+print to stdout the list of build time dependencies
+
+2. The core tools
+All core tools read configuration data frmo stdin and perform some action
+
+2.1 sources
+read the following from stdin
+ chroot path c
+ build path T
+ build path in chroot Tc
+ result name r
+and put everything required to build the result into the build directory:
+ sources
+ dependencies
+ the runbuild script
+ the build-script
+
+2.2 runbuild
+read the following from stdin
+ build path T
+ result name r
+and run the build script for the result
+
+2.3 finish
+read the following from stdin
+ chroot path c
+ build path T
+ build path in chroot Tc
+ result name r
+fetch the result file from the build directory
+
+2.4 cleanup
+read the following from stdin
+ chroot path c
+ build path T
+ build path in chroot Tc
+ result name r
+and clean up the build directory
+
+2.5 all
+read the following from stdin
+ chroot path c
+ build path T
+ build path in chroot Tc
+ result name r
+and run all steps required to build without using a chroot environment
+
+3. The wrapper tools
+
+3.1 build-config
+read a result name from stdin
+print the following to stdout for use as input to most build tools
+ chroot path c
+ build path T
+ build path in chroot Tc
+ result name r
+
+3.2 build-multiple
+read one result name per line from stdin until EOF is reached
+run 'build-config | all' for each result
+
+3.3 build-multiple-chroot
+read one result name per line from stdin until EOF is reached
+run 'build-config | all-chroot' from each result
+
+3.4 build
+build the project while using tagged source, heads of selected branches
+of any available git repositories, or the local working copy
+
+3.5 fetch-git-source
+read a source name from stdin and checkout all git repositories for the source
+
+3.6 fetch-project
+checkout the project given on the command line
+
+4. The chroot tools
+All chroot tools read the following from stdin:
+ chroot path c
+ build path T
+ build path in chroot Tc
+ result name r
+
+4.1 chroot-setup
+build the chroot environment
+
+4.2 chroot-cleanup
+remove the chroot environment
+
+4.3 all-chroot
+and run all steps required to build inside the chroot environment
+
+5. Helper tools
+5.1 readconfig
+read a file from parameter and filter comments
+
+5.2 locate-project-dir
+walk up the directory hierarchy and check if we are inside an e2 project
+if successful print the following to stdout:
+ the current working directory
+ the basename of the current working directory
+ the project directory
+ the path relative to the project directory
+ the absolute path to the projects' bin/ directory
+
+5.3 e2
+e2 is the main script that calls all local tools. e2 refuses to call a
+local tool if the cwd is not inside a project directory.
+Local tools are called from ./bin in the project root unless the
+E2_LOCAL_TOOL_PATH environment variable is set.
+
+6. Bootstrap process to check out a project and the corresponding
+ version of the e2 tool
+
+6.1 steps
+ 1. fetch the e2 tool and perform a global install
+ 2. clone the project repository (git clone)
+ .e2/e2version contain information about the corresponding e2 tool version
+ 3. fetch the corresponding version of the e2 tool repository to .e2/
+ (git clone)
+ 4. build and install the e2 tool inside the project (make; make install)
+
+6.2 global tools
+ e2-su
+ e2-bootstrap-project (steps 2,3,4)
+ e2
+
+6.3 project tools
+ e2-ls-project
+
+7. Hacking e2
+This chapter describes how to make hacking the e2 tool convenient.
+
+7.1 (re)install a development version of e2
+ install the global tool
+ $ make install-global
+ install symlinks to the local tools in the e2 working tree
+ $ make install-local-symlinks LOCALBINDIR=$PWD/devel
+ use the development tools while testing
+ $ export E2_LOCAL_TOOL_PATH=$PWD/devel
+ It is possible to edit the local tools in the working copy and immediately
+ use them in any project without performing additional steps ('make install').
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+SUBDIRS =
+TOPLEVEL = ../..
+
+DIR = $(shell pwd)
+
+include $(TOPLEVEL)/make.vars
+
+CLEAN_FILES = *~ luadoc.tar.gz
+
+.PHONY: all e2commit install install-local clean local localdist uninstall \
+ doc
+
+all:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+uninstall:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+local:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-local:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+doc: luadoc.tar.gz
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc: doc
+ tar -C $(DOCDIR) -xzf luadoc.tar.gz
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ; \
+ done
+
+luadoc.tar.gz: $(TOPLEVEL)/local/*.lua $(TOPLEVEL)/generic/*.lua
+ rm -fr luadoc
+ mkdir luadoc
+ (cd $(TOPLEVEL) ; luadoc -d $(DIR)/luadoc \
+ local/*.lua \
+ generic/*.lua)
+ tar -czf luadoc.tar.gz luadoc
+
+clean:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+ rm -f $(CLEAN_FILES)
+ rm -fr luadoc
+
+check:
+ for s in $(SUBDIRS) do ;\
+ $(MAKE) -C $$s $@ ;\
+ done
+
+localdist:
+ for s in $(SUBDIRS) do ;\
+ $(MAKE) -C $$s $@ ;\
+ done
+
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+TOPLEVEL = ../..
+
+include $(TOPLEVEL)/make.vars
+
+MANPAGES1 = \
+e2.1 \
+e2-build.1 \
+e2-buildid.1 \
+e2-cf.1 \
+e2-create-project.1 \
+e2-dlist.1 \
+e2-dsort.1 \
+e2-fetch-project.1 \
+e2-fetch-sources.1 \
+e2-install-e2.1 \
+e2-ls-project.1 \
+e2-new-source.1 \
+e2-playground.1 \
+e2ssh.1 \
+e2.conf.5 \
+e2-build-numbers.1
+
+MANPAGES5 = e2rc.5 e2config.5
+MANPAGES7 = e2global.7 e2hooks.7 e2intro.7 e2workflow.7
+MANPAGES = $(MANPAGES1) $(MANPAGES5) $(MANPAGES7)
+
+MANPAGES_HTML = $(MANPAGES:=.html)
+
+CLEAN_FILES = *~ man.tar.gz $(MANPAGES_HTML)
+
+.PHONY: html man doc install-doc
+
+all:
+
+install:
+ mkdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 $(DESTDIR)$(MANDIR)/man7
+ install -m644 $(MANPAGES1) $(DESTDIR)$(MANDIR)/man1
+ install -m644 $(MANPAGES5) $(DESTDIR)$(MANDIR)/man5
+ install -m644 $(MANPAGES7) $(DESTDIR)$(MANDIR)/man7
+
+uninstall:
+ for x in $(MANPAGES1) ; do rm -f $(DESTDIR)$(MANDIR)/man1/$$x ; done
+ for x in $(MANPAGES5) ; do rm -f $(DESTDIR)$(MANDIR)/man5/$$x ; done
+ for x in $(MANPAGES7) ; do rm -f $(DESTDIR)$(MANDIR)/man7/$$x ; done
+
+doc: man.tar.gz
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc: doc
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+ tar -C $(DOCDIR) -xzf man.tar.gz
+
+clean:
+ rm -f $(CLEAN_FILES)
+ rm -fr html man
+
+%.html: %
+ man2html -r $< >$@
+
+man.tar.gz: $(MANPAGES_HTML)
+ mkdir -p html man/man{1,5,7}
+ cp *.1.html man/man1/
+ cp *.5.html man/man5/
+ cp *.7.html man/man7/
+ tar -czf man.tar.gz man
--- /dev/null
+.\" Man page for e2-build-numbers
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-build-numbers 1 "Jun 17, 2008" "0.1"
+
+.SH NAME
+e2-build-numbers \- updates build-numbers for one, multiple or all results
+
+.SH SYNOPSIS
+e2-build-numbers [OPTION | RESULT ...]
+
+.SH DESCRIPTION
+This tool allows creating and maintaining a monotonically increasing
+build-number for each result. The build-numbers, together with the
+build-id is stored in the local project-file "proj/build-numbers".
+The build-number can be manually increased or synchronized with
+a central server. Build-numbers always strictly increase with each
+change. After synchronization, an environment file with suitable
+variable definitions is generated in "proj/build-numbers.env". This
+file can be included in "proj/env" with the following command:
+
+.nf
+ env "proj/build-numbers.env"
+.fi
+
+This tool implicitly operates in "tag" mode, that is, it computes
+the results fingerprint with sources specified in the "tag" field
+of the source configuration, if the source refers to version-controlled
+files.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-no\-sync
+Do not synchronize with the server, just update local build-number file.
+.TP
+.BR
+.BR \-\-prefix=PREFIX
+Add a user-defined prefix before the local build-numbers.
+.TP
+.BR \-\-force
+Force increasing the build number for the results given on the
+command line, or all, if no results have been specified.
+.TP
+.BR \-\-dry\-run
+Do not change any files.
+.TP
+.BR \-\-help
+Show usage information.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-build (1).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-build
+.\"
+.\" (c)2007, 2008 emlix GmbH
+.\"
+.TH e2-build 1 "Dec 1, 2008" "2.2"
+
+.SH NAME
+e2-build \- build results from repository or local sources
+
+.SH SYNOPSIS
+e2-build [OPTION|RESULT ...]
+
+.SH DESCRIPTION
+\fBe2-build\fR builds one or more results,
+dependencies are taken into account.
+Depending on the intended outcome, different versions of the source
+files to use may be retrieved for the build process.
+See \fBe2config\fR for a description of the source config file format.
+Invokes the hooks "pre-build" and "post-build".
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-build-mode=MODE
+results can be built according to different build modes, as defined by the
+(currently builtin) policy module.
+A build mode sets the class of sources to take, and influences the buildid,
+the dependencies used and the place to store results.
+.TP
+.BR \-\-branch-mode
+build selected results in 'branch' build-mode.
+.TP
+.BR \-\-wc-mode
+build selected results in 'working-copy' build-mode.
+.TP
+.BR \-\-all
+Possibly build all results listed in the dependencies.
+This is the default when building from repository sources.
+.TP
+.BR \-\-playground
+Prepare build environment but do not start the build script. The build
+environment can later be entered using the \fBe2-playground\fR tool.
+.TP
+.BR \-\-keep
+Do not remove the \fBchroot\fR(1) environment after a build (regardless
+of whether it is successful or not).
+.TP
+.BR \-\-help
+Show usage information.
+
+.P
+Valid build modes according to the builtin policy are
+\fBworking-copy\fR, \fBtag\fR, \fBrelease\fR and \fBbranch\fR.
+\fBtag\fR is the default build mode. \fBrelease\fR is exactly the same
+as \fBtag\fR but the results will be stored in a special location on the
+server side. (To be archived, separated from lots of results built
+for testing.)
+
+.P
+To use the branch from the scm source configuration for selected results
+build in 'tag' mode, and select the results to build in branch-mode.
+
+.SH Examples:
+build selected results from their source branches. Dependencies not selected
+on the command line are built in the default build-mode.
+.IP
+e2-build --branch-mode <result> ...
+.PP
+build a release
+.IP
+e2-build --build-mode=release
+.PP
+build
+.IP
+e2-build
+.PP
+
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-ls-project (1),
+.BR e2-playground (1),
+.BR e2config (5),
+.BR e2hooks (7),
+.BR e2-sync-results (1),
+.BR e2-runbuild (1).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007, 2008 emlix GmbH
--- /dev/null
+.\" Man page for e2-buildid
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-buildid 1 "Aug 2, 2007" "0.1"
+
+.SH NAME
+e2-buildid \- computes the build-id for a given result
+
+.SH SYNOPSIS
+e2-buildid [OPTION | RESULT ...]
+
+.SH DESCRIPTION
+To avoid rebuilding whole projects everytime a change is done, for
+each distinct result in a project, a finger print for its sources and
+configuration is calculated, called "build id". Result files are
+stored in a sub directory to "out/<result>", which is named after the
+build-id. This directory is checked to see whether a single result
+must be rebuilt due to source changes. Note that the build-id of
+dependencies of a result influence the id of the result itself.
+
+For temporary results (mode local), the build id is always the dummy
+value "last".
+
+The algorithm for the build id is as follows:
+
+.nf
+ bhash(result) :- sha1sum(for r in {dlist(result), result} phash(r))
+ dlist(result) :- dependencies for result, including indirect
+ phash(result) :- sha1sum(bchroot(result),
+ for f in {proj/chroot,
+ proj/env,
+ proj/init/*,
+ proj/hooks/*,
+ res/result/build-script,
+ res/result/config:files
+ } sha1sum(f),
+ for s in sources revisionid(s))
+ bchroot(result) :- sha1sum(for g in chrootgroups[result] ghash(g))
+ ghash(group) :- sha1sum(for e in chroot/group: server, file)
+ revisionid(source:files) :- sha1sum(
+ for f in expanded_files_list(source) sha1sum(f) + basename(f))
+.fi
+
+The tool \fBe2-ls-project\fR(1) uses the build-id algorithm to check
+whether results have been built, when called with the "--status" flag.
+
+\fBe2-buildid\fR writes the build-id for the result and in the build mode
+passed in the "RESULT" and "MODE" arguments to standard output.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-all
+Compute build-ids for all results (default unless for working copy).
+.TP
+.BR
+.BR \-\-tag
+Compute build-id based on tagged sources from repository.
+.TP
+.BR \-\-head
+Compute build-id for head revision of sources from repository.
+.TP
+.BR \-\-working\-copy
+Show build-id for local working copies: 'last' always.
+.TP
+.BR \-\-ignore\-env=VARIABLE
+Ignore a particular project environment variable when computing the build-id.
+.TP
+.BR \-\-help
+Show usage information.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-ls-project (1).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for <tool>
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH <tool> 1 "Dec 12, 2007" "2.1"
+
+.SH NAME
+e2-cf \- modify and create configuration files
+
+.SH SYNOPSIS
+usage: e2 cf <command> ...
+
+.SH DESCRIPTION
+commands are:
+ newresult <name>
+ newsource <name> <scm>
+ editresult <name>
+ editbuildscript <name>
+ editsource <name>
+
+Commands starting with 'edit' can be abbreviated by using e...
+Commands starting with 'new' can be abbreviated by using n...
+Commands can be shortened as long as they remain unambiguous
+
+e.g.: eb <name> is equivalent to editbuildscript <name>
+
+Templates are used as initial config files. An editor is started
+for each file that must be configured by the user.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH EXAMPLES
+The following three commands do exactly the same: Create a new result
+configuration for a result named 'foo':
+.IP
+.nf
+e2-cf newresult foo
+e2-cf newr foo
+e2-cf nr foo
+.PP
+
+.SH ENVIRONMENT
+e2-cf uses the EDITOR environment variable to determine which editor to
+start. The default editor is 'vi'.
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-create-project
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-create-project 1 "Aug 6, 2007" "0.1"
+
+.SH NAME
+e2-create-project \- create a new project
+
+.SH SYNOPSIS
+e2-create-project [<option> ...] [<server>:]<location>
+
+.SH DESCRIPTION
+Create a new project and store it on <server> in <location>.
+<server> defaults to 'projects'.
+The project name will be the basename of <location>.
+The usual project location convention is
+\fBprojects:<customer>/<projectname>\fR.
+e2-create-project invokes the "create-project" hook.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.TP
+.BR \-\-version
+Show version number of e2.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+.BR e2global(7)
+.BR e2-fetch-project(1)
+.BR e2-fetch-sources(1)
+.BR e2-ls-project(1)
+.BR e2config(5)
+.BR e2hooks(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-dlist
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-dlist 1 "Aug 3, 2007" "0.1"
+
+.SH NAME
+e2-dlist \- list dependencies of a particular result
+
+.SH SYNOPSIS
+e2-dlist [OPTION|RESULT ...]
+
+.SH DESCRIPTION
+\fBe2-dlist\fR shows the direct dependencies of a result, i.e. the
+results that are required to be built before the argument result can
+be created. In other words, the listed results are
+\fBbuild-time dependencies\fR of the argument result.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-recursive
+Show indirect dependencies as well. Normally only direct dependencies
+are shown.
+.TP
+.BR \-\-help
+Show usage information.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-dsort (1),
+.BR e2-ls-project (1),
+.BR e2config (5).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-dsort
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-dsort 1 "Aug 3, 2007" "0.1"
+
+.SH NAME
+e2-dsort \- sort result dependencies
+
+.SH SYNOPSIS
+e2-dsort [OPTION ...]
+
+.SH DESCRIPTION
+\fBe2-dsort\fR lists the results of the current project, sorted in the
+order in which they must be built.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-dlist (1),
+.BR e2-ls-project (1),
+.BR e2config (5).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-fetch-project
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-fetch-project 1 "Aug 6, 2007" "0.1"
+
+.SH NAME
+e2-fetch-project \- retrieve project file structure
+
+.SH SYNOPSIS
+e2-fetch-project [OPTION]... PATH [DESTINATION]
+
+.SH DESCRIPTION
+\fBe2-fetch-project\fR clones a project repository designated by "PATH",
+which should be the absolute path to a \fBe2(7)\fR project. The usual
+project path convention is \fB/<server-project-dir>/<customer>/<projectname>\fR.
+Invokes the "fetch-project" hook.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.TP
+.BR \-\-version
+Show version number of e2.
+.TP
+.BR \-\-binary
+Installs precompiled local tools.
+.TP
+.BR \-\-branch=BRANCHNAME
+Checks out a specfic project branch.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+.BR e2global(7)
+.BR e2-create-project(1)
+.BR e2-install-e2(1)
+.BR e2-fetch-sources(1)
+.BR e2-ls-project(1)
+.BR e2config(5)
+.BR e2hooks(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-fetch-sources
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-fetch-sources 1 "Aug 3, 2007" "0.1"
+
+.SH NAME
+e2-fetch-sources \- retrieve sources for the current project
+
+.SH SYNOPSIS
+e2-fetch-sources [OPTION|SOURCE|RESULT ...]
+
+.SH DESCRIPTION
+\fBe2-fetch-sources\fR retrieves sources for project results. Sources can be
+either specified through the source- or result names. For sources stored
+in version-controlled repositories, the associated repositories will be
+cloned and a project-local working copy created. Invokes the
+"fetch-sources" hook.
+
+Note, that sources of type \fIfiles\fR are not fetched.
+
+Note, that sources are not fetched again, where fetched yet.
+Moreover, the local working copy at \fIin/SOURCE\fR is not checked for
+up-to-dateness.
+Thus, to refetch sources, remove the outdated working copy,
+then fetch again.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-all
+Fetch all sources refered to in the whole project.
+.TP
+.BR \-\-source
+Fetch sources by source name.
+.TP
+.BR \-\-result
+Fetch sources by result name.
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-ls-project (1),
+.BR e2config (5),
+.BR e2hooks (7).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-install-e2
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-install-e2 1 "Sep 11, 2007" "0.2"
+
+.SH NAME
+e2-install-e2 \- install the e2 local tools into a project environment
+
+.SH SYNOPSIS
+e2-install-e2
+
+.SH DESCRIPTION
+\fBe2-install-e2\fR reads the required e2 version from the
+\fB.e2/e2version\fR file and installs that version of e2 into the local
+project environment.
+
+The \fB.e2/e2version\fR file has a single line with this format:
+
+ <branch> <tag>
+
+By default the tagged version of e2 is installed.
+
+To enable the project developers to follow a stable branch of the
+e2 local tools, the tag may be ^ unless a release shall be built.
+In this case the HEAD of the branch is installed. In this case
+the e2 local tools change if commits are done to that branch.
+
+That is analog to source configurations in the project repository.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.TP
+.BR \-\-version
+Show version number of e2.
+.TP
+.BR \-\-binary
+Installs precompiled local tools.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+.BR e2global(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-ls-project
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-ls-project 1 "Aug 6, 2007" "0.1"
+
+.SH NAME
+e2-ls-project \- show project information
+
+.SH SYNOPSIS
+e2-ls-project [OPTION]...
+
+.SH DESCRIPTION
+\fBe2-ls-project\fR displays the configuration and components of the
+current project and can report the current build status of results.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-status
+Show build status of all results.
+.TP
+.BR \-\-all
+Show all sources and results, even if not currently used by the project.
+.TP
+.BR \-\-offline
+Do not check or print data that reside on a server.
+.TP
+.BR \-\-dot
+Generate \fBdot\fR(1) graph from source and result dependencies.
+.TP
+.BR \-\-licences
+Lists licences used in this project and their paths.
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2config (5).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-new-source
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-new-source 1 "Oct 23, 2007" "0.2"
+
+.SH NAME
+e2-new-source \- create a new source at a shared server location
+
+.SH SYNOPSIS
+e2-new-source --git [--server <server>] <name>
+
+e2-new-source --files [--no-checksum] [<server>:]<location> <source_file> [<checksum_file>]
+
+.SH DESCRIPTION
+\fBe2-new-source\fR creates a new source repository or stores a file plus
+checksum file in a location on a server.
+
+The type of the new source must be given, either --files or --git
+cvs and subversion do not support auto-creation of repositories for now.
+
+When creating a git repository a server side repository is created and
+a local repository is created and set up to be related to the server side
+repository via the remote named origin.
+No commits are initially created. The user must create the initial commit
+and push it to the server. See Examples below.
+
+e2-new-source(1) does not create any configuration files. Use e2-cf(1) to
+create a source configuration.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-git
+Create a \fBgit\fR(7) repository at SERVERPATH.
+.TP
+.BR \-\-files
+Create a \fBtar\fR(1) archive file source at SERVERPATH.
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH EXAMPLES
+To place an external source file on the default server:
+.IP
+e2-new-source --files a/anything/0.1 file:///tmp/download/anything-0.1.tar.bz2
+file:///tmp/download/anything-0.1.tar.bz2.sha1
+.PP
+To create a new, empty git repository on server and client side:
+.IP
+e2-new-source --git test2
+.PP
+To populate the empty repository from an existing repository:
+.IP
+.nf
+cd in/<source>
+git fetch /path/to/original/repo <branch>:refs/heads/master
+git push origin master
+.fi
+.PP
+To populate the empty repositories with new sources:
+.IP
+.nf
+cd in/<source>
+# create source files, e.g. main.c
+git add .
+git commit -m 'initial commit'
+git push origin master
+.fi
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-ls-project (1),
+.BR e2config (5).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2-playground
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-playground 1 "Aug 9, 2007" "0.1"
+
+.SH NAME
+e2-playground \- enter chroot(1) environment for specified result
+
+
+.SH SYNOPSIS
+e2-playground OPTION|RESULT ...
+
+.SH DESCRIPTION
+\fBe2-playground\fR enters a \fBchroot\fR(1) environment previously
+created by running \fBe2-build\fR with the "--playground" option, or
+one that is left over from a previous (possibly failed) build.
+Calls the "enter-playground" hook.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-command\=CMD
+Execute \fBCMD\fR in the chroot(1) environment, instead of running
+an interactive shell.
+.TP
+.BR \-\-showpath
+Print path to chroot(1) environment.
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7),
+.BR e2-ls-project (1),
+.BR e2-build (1),
+.BR e2config (5),
+.BR e2hooks (7).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2 1 "Aug 2, 2007" "0.1"
+
+.SH NAME
+e2 \- wrapper script for e2 build tools
+
+.SH SYNOPSIS
+e2 OPTION ...
+.br
+e2 COMMAND ARGUMENT ...
+
+.SH DESCRIPTION
+\fBe2\fR is a wrapper script for invoking \fBe2\fR commands. Any
+\fBe2\fR commands called will be redirected either to the
+project-local or the global installation, depending on whether the
+invocation took place inside or outside a project tree.
+
+.SH RETURN VALUE
+Depends on invoked subtool or 0 if an unknown command was given.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.TP
+.BR \-\-version
+Show version number of e2.
+.TP
+.BR \-\-prefix
+Print installation prefix and exit.
+
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for <tool>
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH <tool> 1 "Aug 15, 2008" "2.2"
+
+.SH NAME
+e2.conf - global configuration
+
+.SH DESCRIPTION
+\fBe2.conf\fR is the global configuration file holding tool and server
+configurations.
+
+.SH EXAMPLE
+.nf
+config {
+ tools = {
+ which = { name="which" },
+ curl = { name="curl" },
+ ssh = { name="ssh" },
+ scp = { name="scp" },
+ cvs = { name="cvs", flags="" },
+ },
+ servers = {
+ upstream = {
+ url = "ssh://e2data/data/e2data/upstream",
+ cachable = true,
+ cache = true,
+ },
+ projects = {
+ url = "ssh://e2data/mnt/e2data/projects",
+ cachable = false,
+ cache = false,
+ },
+ chroot = {
+ url = "ssh://e2data/mnt/e2data/results",
+ cachable = true,
+ cache = true,
+ },
+ results = {
+ url = "ssh://e2data/data/e2data/results",
+ cachable = true,
+ cache = true,
+ },
+ },
+}
+.nf
+
+.SH "SEE ALSO"
+.BR e2config(5)
+.BR e2intro (7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2008 emlix GmbH
--- /dev/null
+.\" Man page for e2config
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2config 1 "Aug 6, 2007" "0.1"
+
+.SH NAME
+e2config \- e2 configuration file format
+
+.SH DESCRIPTION
+An \fBe2\fR project tree consists of the following files and
+sub-directories:
+
+.nf
+.
+|-- .e2 local part of e2 tool
+|-- in sources from shared repositories
+|-- out will hold build results
+|-- proj project configuration directory
+| |-- env holds project environment (optional)
+| |-- chroot required chroot tarballs
+| |-- default-results main final results of the project
+| |-- init initialization scripts run inside chroot
+| |-- hooks hook scripts for customization (optional)
+| |-- licences list of licences used by sources (optional)
+| |-- name project name
+| |-- result-storage where to store shared build-results
+| o-- servers remote servers for sources and projects
+|
+|-- log contains log files for e2 use and builds
+| |-- debug.log e2 internal log for last operation
+| |-- build.<result>.log build log for specific result
+| :
+|
+|-- res build result descriptions
+| |-- <result> one directory for each result
+| | |-- config result configuration file
+| | |-- build-script build instructions executed in chroot
+| | o-- <local file> project-local files (related to result)
+| :
+|
+o-- src source descriptions for each source
+ |-- <source> one directory for each source
+ | |-- config source configuration
+ | o-- <local file> project-local files (related to source)
+ :
+.fi
+
+.SH DEPENDENCY\ CONFIGURATION
+
+The file "proj/default-results" lists the main final results of
+the project, one per line. This is for convenience only, the list
+is used when e2-build is called with no result name.
+
+.SH ENVIRONMENT\ CONFIGURATION
+
+The optional file "proj/env" may define a table of global variables that are
+accessible during the build process. The table should be of the form
+
+.nf
+env {
+ <variable1> = "<value1>",
+ <variable2> = "<value2>",
+ :
+}
+.fi
+
+During execution of a build script for a given result, environment variables
+of the same name will be defined, so in this example a build script could
+access the variables \fB$variable1\fR and \fB$variable2\fR. Variable values
+must be strings or symbols. The environment table is stored in the global
+Lua variable \fBenv\fR and can be referred to from source- and result configuration
+file (using \fBenv.variable1\R, \fBenv.variable2\fR, ...).
+
+Per-result environment variable entries can be defined with the syntax
+
+.nf
+env {
+ :
+ <resultname> = { <variable1 = "<value1>", ... }
+ :
+}
+.fi
+
+In this case the defined variables are only available in the build-script
+for this particular result. Note that \fB<resultname\fR must be a valid
+variable name. If the result has a name containing special characters
+(like "-"), use this syntax:
+
+.nf
+env {
+ :
+ ["<resultname>"] = { ... }
+ :
+}
+.fi
+
+Accessing a result-specific variable must be done using this syntax:
+
+.nf
+ env[ "<resultname>" ].<variable>
+.fi
+
+\fBenv\fR may also be invoked with a single string as argument. In
+this case the string should designate a path (relative to the project
+directory) with another environment definition file in the same syntax.
+This file will then be loaded and can add additional definitions to
+the project environment.
+
+.SH CHROOT\ CONFIGURATION
+
+The file "proj/chroot" lists the binaries
+that are copied into the \fBchroot(1)\fR
+environment before the build of a result commences.
+The chroot configuration file should have the following format:
+
+.nf
+e2chroot {
+ {
+ group = "<groupname>",
+ server = "<servername>",
+ files = {
+ "<path-to-file>",
+ ...
+ },
+ },
+ ...
+}
+.fi
+
+The group name is arbitrary and is used
+to select particular sets of chroot binaries in a result configuration.
+
+.SH SERVER\ CONFIGURATION
+
+The file "proj/servers" holds a list of abstract server names. Each
+line should contain two fields, separated by whitespace: the server
+name and the path where the server's resources are stored. A relative
+path is always treated as relative to the projects root directory.
+Empty lines and lines starting with the "#" symbol are ignored.
+
+.SH LICENCES\ CONFIGURATION
+
+The licences file (proj/licences) should have the following format:
+
+.nf
+e2licence {
+ <licencename> = {
+ server = "<servername>",
+ name = "<path-to-directory>"
+ }
+}
+.fi
+
+All licences explicitely referenced in source configuration
+shall be listed in this file.
+Each entry refers to a directory, which may contain one or
+more files that make up the licence information.
+
+The licence configuration file must follow \fBlua(1)\fR syntax.
+
+.SH SOURCE\ CONFIGURATION
+
+The "config" file for a source should have the following general
+form:
+
+.nf
+e2source {
+ <propertyname> = <value>,
+ ...
+}
+.fi
+
+If the source refers to a tarball or fixed set of files, the following
+properties should be set:
+
+.nf
+file = {
+ { server = "<servername>",
+ name = "<path-to-file>",
+ <destiny> = "contents"
+ }
+}
+.fi
+
+<destiny> is one of \fBunpack\fR, \fBpatch\fR or \fBcopy\fR.
+The contents depends on the type of destiny.
+For \fBunpack\fR it is the single subdir contained in the tarball to unpack,
+for \fBpatch\fR it is the patch level to apply with, usually \fI1\fR,
+for \fBcopy\fR it is the destination path, relative to the source subdir.
+
+For example:
+
+.nf
+e2source {
+ file={
+ { server="upstream",
+ name="hello/1.0/hello-1.0.tar.gz",
+ unpack="hello-1.0"
+ }
+ }
+}
+.fi
+
+If the source refers to a version control repository
+(\fBgit(7)\fR, \fBsvn(1)\fR, etc.), then
+these properties should be defined:
+
+.nf
+branch = "<branchname>" (usually "master")
+remote = "<path-to-repository>"
+server = "<servername>"
+tag = "<tagname>"
+type = "<scmtype>" ("git", "svn" or "cvs")
+working = "<path-to-working-dir>" (usually "in/<sourcename>")
+licence = "<licencename>" (as listed in proj/licences)
+.fi
+
+If the source refers to a \fBcvs\fR repository, the following applies,
+in addition to the properties given above:
+
+.nf
+remote must not be configured
+cvsroot = "<path to cvsroot>"
+module = "<module path>"
+.fi
+
+To use cvs pserver mode the server must use "cvspserver://" transport.
+cvs login is not supported. Do cvs login before trying to access the
+cvs server with e2.
+
+Source configuration files must follow \fBlua(1)\fR syntax.
+
+The tag field may provide a list of tags instead of a single tag.
+The last tag in the list is the current one used when building.
+The other tags may be kept for historical purpose, they are used
+to split up the source when preparing it for cargo.
+
+A config file referring to a version control repository may
+contain the field "file", which states the historical basic state
+of the source, which will be used to split up source upon preparation
+for cargo into the old files state and newer tagged patches.
+
+The licence entry value is used as key into the table
+in proj/licences.
+It is optional for a source of type \fIfiles\fR,
+but obligatory otherwise.
+
+Note that all references to a server accept the special server name "."
+(period) which refers to the project directory itself.
+E.g., the following declaration refers to a kernel configuration file
+stored locally in the project:
+
+.nf
+e2source {
+ file={
+ { server = ".",
+ name = "src/linux-config/my_config",
+ copy = ".config"
+ }
+ }
+}
+.fi
+
+Note that during development the \fItagname\fR may be replaced by \fB^\fR
+to denote the head revision of the given branch \fIbranchname\fR.
+
+.SH RESULT\ CONFIGURATION
+
+"config" files for results look similar to source configurations, but
+use the "e2result" configuration command:
+
+.nf
+e2result {
+ <propertyname> = <value>,
+ ...
+}
+.fi
+
+Value result properties are:
+
+.nf
+sources = { "<sourcename1>", ... } (zero or more sources by name)
+files = { "<resultfilename1>", ... } (i.e. "hello.tar.gz")
+chroot = { "<chroot-group1>", ... } (defaults to all groups)
+depends = { "<dependency1>", ... } (defaults to none)
+.fi
+
+The "sources" property specifies the archives, repositories, files or
+patches referenced by configuration entries in the "src" directory
+which are to be copied into the build environment. Only the sources
+given in this property will be copied/extracted.
+
+The properties may omit the "{ ... }" braces, if only a single entry is
+used. Note that you can specify multiple sources for any single result.
+
+Result configuration files must follow \fBlua(1)\fR syntax.
+
+.SH CONFIGURATION\ FILE\ RESTRICTIONS
+
+Configuration files (specifically source-configurations,
+result-configurations and the project environment) are executed in a
+restricted environment: only the confguration-specific initializer
+functions (\fBe2source\fR, \fBe2result\fR and \fBenv\fR) and the Lua
+standard operations from the \fBstring\fR are available). Adding new
+definitions to the global environment is not allowed. Note that
+re-definitions are still currently possible. Hooks do not run in
+restricted mode and have full access to the e2 internals.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+.BR e2-ls-project(1)
+.BR e2.conf(5)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page e2-global
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2-global 7 "Sep 11, 2007" "0.2"
+
+.SH NAME
+e2global \- e2 global tools
+
+.SH DESCRIPTION
+There is a set of global tools with a well-defined interface to
+projects stored on a server and projects checked out as a working
+copy.
+This interface is versioned using files named \fBversion\fR in the
+project directory on the server, and \fB.e2/version\fR in the project
+repository.
+These files store integer version ids. They state which version of
+global code may be used to perform certain tasks on a project.
+
+.nf
+The interfaces cover:
+ * how to create a project
+ * how to checkout a project from the server
+ * how to install the local e2 tools into a project
+ * how to call local tools
+.fi
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2-fetch-sources(1)
+.BR e2-create-project(1)
+.BR e2(1)
+.BR e2intro(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2 hooks
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2hook 7 "Aug 2, 2007" "0.1"
+
+.SH NAME
+e2hooks \- Hooks available in the e2 build system
+
+.SH DESCRIPTION
+During execution of \fBe2\fR commands a number of "hook" scripts are
+called, if available, to perform any site-specific customization of
+the build process. Hook scripts are located in the "hooks"
+subdirectory of the project and will be loaded and evaluated as Lua
+code. Global tools do not nocessarily execute in a project directory
+and will load and evaluate hooks in the \fB$HOME/.e2/hooks\fR
+directory, if it exists. Hook-specific arguments are passed as a table
+in the global "e2hook" table under the key "arguments". The project
+info structure (not necessarily complete) is stored under the key
+"info" in the "e2hook" table. Finally, the name of the currently
+invoked tool can be found under "toolname" in the said table.
+
+Note, that hooks are not intended to add functionality changing the
+build process itself. It is an error to let a hook do anything that
+changes the outcome of the build process. Instead, hooks may be used
+to trigger actions in the environment of the build process.
+
+.SH HOOKS
+
+.TP
+.BR tool\-start
+Called when a local tool executes (after command-line processing, but
+before the actual operations begin). Note that the \fBe2hook.info\fR
+entry may be \fBnil\fR if this hook is triggered in a global tool.
+
+.TP
+.BR tool\-finish
+Called after a local tool executed without an error. If an error
+occurred, this hook will not run. Note that the \fBe2hook.info\fR
+entry may be \fBnil\fR if this hook is triggered in a global tool.
+
+.TP
+.BR pre\-build
+Called before an \fBe2-build(1)\fR command commences. Arguments are
+given in a table with the following keys:
+
+.nf
+ info = <project info structure>
+ result = <table of results>
+ tags = <bool>
+ heads = <bool>
+ playground = <resultname or false>
+.fi
+
+.TP
+.BR post\-build
+Called after \fBe2-build(1)\fR finishes, with the same arguments as
+"pre-build".
+
+.TP
+.BR build\-setup\-chroot
+Called after the chroot(1) environment has been set up for building a
+single result. The argument is a build configuration table.
+
+.TP
+.BR build\-pre\-runbuild
+Called before starting the actual build-script inside the chroot(1)
+environment for a single result. The argument is the build configuration table.
+
+.TP
+.BR build\-post\-runbuild
+Called after the build-script of a single result has completed. Argument
+is the build configuration table.
+
+.TP
+.BR build\-remove\-chroot
+Called before the chroot(1) environment for a single result is removed.
+Argument is the build configuration table.
+
+.TP
+.BR build\-pre\-sources
+Called before the sources are applied into the chroot(1) environment for
+a single result. Argument is the build configuration table.
+
+.TP
+.BR build\-post\-sources
+Called after the sources have been applied into the chroot(1) environment.
+
+.TP
+.BR build\-failure
+Called when the build script of a result fails. Argument is the build
+configuration table.
+
+.TP
+.BR files\-prepare\-source
+Called once after preparing each single source file for files sources.
+The argument table carries the entries \fBsource\fR (source name),
+\fBfile\fR (file name) and \fBpath\fR (build path).
+
+.TP
+.BR build\-pre\-result
+Called before a single result is build. Argument is the build configuration
+table.
+
+.TP
+.BR build\-post\-result
+Called after a single result is build. Argument is the build configuration
+table.
+
+.TP
+.BR create\-project
+Called after a base project has been cloned by \fBe2-create-project(1)\fR
+and before it is stored on the project server.
+The argument is the root path of the project. The "info" entry in
+the "e2hook" table is incomplete for this hook.
+
+.TP
+.BR fetch\-project
+Called after fetching a project with \fBe2-fetch-project(1)\fR. The argument
+is the root path of the project. The "info" entry in
+the "e2hook" table is incomplete for this hook.
+
+.TP
+.BR fetch\-sources
+Called after fetching sources with \fBe2-fetch-sources(1)\fR. The argument
+is a table containing the requested sources.
+
+.TP
+.BR enter\-playground
+Called before entering a chroot(1) environment via \fBe2-playground(1)\fR.
+The argument is the path where the chroot environment is located.
+
+.TP
+.BR use\-source
+Called after a new source is inserted into an existing project by
+\fBe2-use-source(1)\fR. The argument is a table with the following entries:
+
+.nf
+ type = <source type string>
+ server = <server name>
+ remote = <server path>
+ working = <working directory>
+ branch = <branch name>
+ tag = <tag name>
+.fi
+
+.SH "HOOK ORDER"
+Build-related hooks are run in the following order:
+
+.nf
+ pre-build
+ build-pre-result (once for each result)
+ build-setup-chroot
+ build-pre-sources
+ build-post-sources
+ build-pre-runbuild
+ build-post-runbuild
+ build-post-result
+ build-remove-chroot
+ post-build
+.fi
+
+.SH BUGS
+
+When \fBe2-new-source\fR is invoked without the \fB--source\fR option,
+then the \fBtool-start\fR and \fBtool-finish\fR hooks will not execute.
+
+Doubtless there are many others.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+.BR e2-build(1)
+.BR e2-create-project(1)
+.BR e2-fetch-project(1)
+.BR e2-fetch-sources(1)
+.BR e2-playground(1)
+.BR e2-use-source(1)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" General man page for e2
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2intro 7 "Aug 3, 2007" "0.1"
+
+.SH NAME
+e2 \- a system for automated and reproducible builds
+
+.SH DESCRIPTION
+\fBe2\fR is a system for building software from sources in a chroot(1)
+environment. The results of a build are fully reproducible and care is
+taken to avoid re-builds unless they would influence the outcome.
+Dependencies among build components are tracked and automatically
+handled by e2.
+
+.SH TERMINOLOGY
+
+.TP
+.BR result
+Designates something that is the product of executing a "build-script"
+with a given set of input "sources". Usually a set of binaries or a
+root file system.
+
+.TP
+.BR source
+Something that is the input to a build process. Usually this means a set
+of source files.
+.TP
+.BR project
+A "project" is a collection of sources, results and a description of their
+interdependencies. When the documentation refers to a \fBcurrent\fR
+project, then the project tree in which a particular e2 command is
+executed is meant.
+.TP
+.BR mode
+A mode in which a result is built, either "tag", "head" or "working-copy".
+.TP
+.BR backing store
+One or more locations that are used to store sources and build results,
+usually shared by several developers.
+
+.SH TOOLS
+e2 is used from the command line and provides the following tools to
+configure a project and run the build process:
+
+.SH GLOBAL TOOLS
+.TP
+.BR e2
+Call local tools.
+.TP
+.BR e2-create-project
+Create a new project in the backing store.
+.TP
+.BR e2-fetch-project
+Retrieves a project from the backing store.
+.TP
+.BR e2-install-e2
+Install the local e2 tools into a project environment.
+.TP
+.BR e2-su-*
+Provide root privileges for some actions.
+
+.SH LOCAL TOOLS
+.TP
+.BR e2-build
+Depending on options, builds a particular result or all,
+either using the state of the sources as stored in the
+current working copy, or the appropriate revision from the
+corresponding repository, when the source is located in a version controlled
+repository.
+.TP
+.BR e2-buildid
+Computes the build id for a given result, in a given build mode.
+.TP
+.BR e2-dlist
+Shows a sorted list of the dependencies for a given result.
+.TP
+.BR e2-dsort
+Shows the sorted list of results in the order in which they must be built
+to satisfy interdependencies among each result.
+.TP
+.BR e2-fetch-sources
+Retrieves the sources for the current project from the backing store.
+.TP
+.BR e2-ls-project
+Displays a schematic view of the current project and shows the project build status.
+.TP
+.BR e2-use-source
+Add a source to the current project.
+.TP
+.BR e2-new-source
+Create a new source in the backing store.
+.TP
+.BR e2-playground
+Enter chroot environment previously created via
+\fBe2-build\ \-\-playground\fR.
+
+.TP
+All tools support the following common set of options:
+
+.TP
+.BR \-\-help
+Show usage information.
+.TP
+.BR \-\-version
+Show e2 version number and exit.
+.TP
+.BR \-\-quiet
+Disable all output to console.
+.TP
+.BR \-\-verbose
+Show information about what the tool is currently doing.
+.TP
+.BR \-\-debug
+Show complete debug information giving as much output as possible,
+as long as it concerns the current project.
+.TP
+.BR \-\-tooldebug
+Show complete debug information, including tool internal information.
+.TP
+.BR \-\-e2\-config
+Specify a e2 config file
+
+.SH ENVIRONMENT
+.TP
+.BR E2_CONFIG
+use a custom e2 config file
+.TP
+.BR E2_SSH
+specify a tool to use instead of \fBssh\fR(1).
+.TP
+.BR E2_SSH_OPTIONS
+set ssh options when using the \fBe2ssh\fR(1) \fBssh\fR wrapper.
+
+.SH "SEE ALSO"
+.BR e2workflow(7)
+.BR e2-build(1)
+.BR e2-buildid(1)
+.BR e2-cf(1)
+.BR e2-dlist(1)
+.BR e2-dsort(1)
+.BR e2-fetch-sources(1)
+.BR e2-ls-project(1)
+.BR e2-use-source(1)
+.BR e2-fetch-project(1)
+.BR e2-new-source(1)
+.BR e2-create-project(1)
+.BR e2config(5)
+.BR e2hooks(7)
+.BR e2-sync-results(1)
+.BR e2global(1)
+.BR e2-install-e2(1)
+.BR e2ssh(1)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2rc
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2rc.5 "Aug 2, 2007" "0.1"
+
+.SH NAME
+e2rc \- per-user default settings for local e2 tools
+
+.SH SYNOPSIS
+e2rc [OPTION/PATH]...
+
+.SH DESCRIPTION
+\fB$HOME/.e2/e2rc\fR has the following format:
+
+ e2rc {
+ { "option" [, value] },
+ ...
+ }
+
+where \fBoption\fR must be the name of a valid command line option and value
+can be \fBtrue\fR or \fBfalse\fR for flags or a quoted string for options
+that take an argument.
+Omitting the value is equivalent to specifying \fBtrue\fR.
+Options given on the command line override options specified in
+\fBe2rc\fR.
+
+Example:
+
+ e2rc {
+ { "debug" }
+ }
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for e2ssh
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2 1 "Aug 12, 2008" "2.2"
+
+.SH NAME
+e2ssh \- wrapper script for ssh to support custom ssh options
+
+.SH SYNOPSIS
+e2ssh SSH_OPTIONS
+
+.SH DESCRIPTION
+\fBe2ssh\fR is a wrapper script that calls ssh passing the arguments
+given in the E2_SSH_OPTIONS environment variable and its own command line
+options to ssh.
+It is intended to be used by setting the E2_SSH environment variable
+to \fBe2ssh\fR when calling e2 tools.
+
+.SH RETURN VALUE
+the return value of ssh
+
+.SH ENVIRONMENT
+.TP
+.BR E2_SSH_OPTIONS
+options given in this variable are passed to \fBssh\fR.
+
+.SH BUGS
+No known bugs.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2008 emlix GmbH
--- /dev/null
+.\" Man page for e2workflow
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH e2workflow 7 "Aug 2, 2007" "0.1"
+
+.SH NAME
+e2workflow \- common use cases and workflows for the e2 build system
+
+.SH "CREATING A NEW PROJECT"
+e2-create-project
+
+.SH "RETRIEVING A PROJECT THAT ALREADY EXISTS"
+e2-fetch-project
+.br
+e2-fetch-sources
+
+.SH "TAKING A LOOK"
+e2-ls-project
+.br
+e2-dlist
+.br
+e2-dsort
+
+.SH "ADDING NEW PACKAGES TO A PROJECT"
+e2-new-source
+.br
+e2-use-source
+
+.SH "BUILDING RESULTS"
+e2-build
+
+.SH "THE PLAYGROUND"
+e2-playground
+
+.SH "SOURCE REPOSITORY MAINTENANCE"
+git/svn
+
+.SH "REMOVING RESULTS OR SOURCES"
+nn
+
+.SH "WRITING HOOKS"
+nn
+
+.SH "USING EXTERNAL REPOSITORIES"
+All data used by the e2 tool is accessed via file system,
+as it is supposed to reside local.
+Where non local data is accessible via \fBnfs\fR(5), it
+can be used, though.
+
+However, an external source repository which is not accessible
+via nfs, is not available to e2 directly.
+It is up to the project coordination to set up a configuration
+where data from an external sources master repository
+is pulled explicitely to a local
+copy of the repository, which then may be used by e2.
+
+.SH "THE RELEASE"
+When software is developed and tested, a release needs to be built.
+This is done with the command \fBe2-build --release\fR.
+However, prior to building the release, all sources and the project
+information itself need to be tagged, to ensure reproducibility
+at a later time. Putting tags onto sources and the project is not
+a task of \fBe2\fR, but is in the scope of the source code management
+in use: First, put tags on all sources, that are kept in repositories.
+The tag names need not to be related to the release name, you may use
+different tag names for the different sources. Then, enter the tag names
+chosen into the appropriate source configuration files: Where the field
+\fBtag\fR is a list of tags, make sure the new tag will be the last in
+the list of tags. When done, commit and tag the project information
+itself. Here it is good practice to choose a tag name directly
+related to the release.
+
+To rebuild a release, that had been build a while ago, check out
+the project information: Fetch the version with the release tag on it.
+Now, you are ready to rebuild the tagged release.
+
+.SH "PROVIDING THE SOURCES"
+With the release done, the sources shall be provided and shipped, too.
+To do so, call \fBe2-prepare-cargo <new-path>\fR to generate a copy
+of the project with no external references left, i.e. no source code
+management and no upstream server references.
+All files needed will be placed inside that new project directory itself.
+Unneeded result and source parts will be dropped,
+unless you give the \fB--all\fR option.
+For each repository based source, where instead of a single tag
+a list of tags is given in the corresponding config file,
+the source provided thru \fBe2-prepare-cargo\fR is split up into
+a basic tar archive and a set of patches to reflect the steps of
+development set with the tags.
+Furthermore, where in a repository based source is given files
+information as with sources of type \fIfiles\fB, this is used
+as basic data set, all tagged versions are provided as patches
+to be applied upon it.
+
+.SH "SEE ALSO"
+.BR e2intro(7)
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+.\" Man page for <tool>
+.\"
+.\" (c)2007 emlix GmbH
+.\"
+.TH <tool> 1 "Dec 12, 2007" "2.1"
+
+.SH NAME
+<tool> \- <description>
+
+.SH SYNOPSIS
+<tool> [OPTION/PATH]...
+
+.SH DESCRIPTION
+\fB<tool>\fR blablabla.
+
+.SH RETURN VALUE
+Normally, exit status is 0. On error, it is non-zero.
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Show usage information.
+.P
+For further global options, see \fBe2intro\fR(7).
+
+.SH BUGS
+Doubtless there are many.
+
+.SH "SEE ALSO"
+.BR e2intro (7).
+
+.SH AUTHORS
+Gordon Hecker, Oskar Schirmer and Felix Winkelmann
+
+.SH COPYRIGHT
+(c)2007 emlix GmbH
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+SUBDIRS =
+TOPLEVEL = ../..
+
+include $(TOPLEVEL)/make.vars
+
+HTML = manual.html \
+ basic-concepts.html buildid.html \
+ environment.html project.html chroot.html licences.html \
+ sources.html results.html release.html migration.html \
+ faq.html
+
+IMAGES = e2factory_logo.png buildid.png
+
+.PHONY: all install local install-local clean localdist uninstall doc
+
+default: doc
+
+all:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+uninstall:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+local:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-local:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+doc: manual.tar.gz
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc: $(HTML)
+ install -d -m 755 $(DOCDIR)/manual
+ for f in $(HTML) ; do \
+ install -m 644 $$f $(DOCDIR)/manual/ ; \
+ done
+ for f in $(IMAGES) ; do \
+ install -m 644 $$f $(DOCDIR)/manual/ ; \
+ done
+ install -m 644 manual.css $(DOCDIR)/manual/
+
+%.html: %.text header.html footer.html
+ cat header.html >$@
+ markdown $< >>$@
+ cat footer.html >>$@
+
+manual.tar.gz: $(HTML)
+ make DOCDIR=./tmp install-doc
+ cp tmp/manual/manual.html tmp/manual/index.html
+ tar -C tmp -czf manual.tar.gz manual
+ rm -fr tmp
+
+clean:
+ rm -f $(HTML) manual.tar.gz
--- /dev/null
+Basic Concepts
+--------------
+
+
+*e2factory* is a tool about reliably building results from sources. That is a short
+and correct description. But let's have a look step-by-step.
+
+Building embedded systems from source usually means taking a lot of steps,
+building different parts of the system, starting with a toolchain, going
+on building the root filesystem and finally creating a flash image, for
+example. You may even want to build an up-to-date set of documentation
+with the final result.
+
+The items mentionen above, toolchain, root filesystem and flash image,
+are usually results in *e2factory*. Each of them usually consists of one
+or more other results, a compiler and assembler and the c library will
+make up the toolchain while the root filesystem could consist of some
+libraries, e.g. zlib and the busybox.
+
+Zlib and busybox are usually results again, but they are built from
+sources (that may or may not be named like the results in those cases).
+
+Thats basically the whole concept: Configure sources and configure
+results using sources and other results as dependencies and *e2factory*
+will build the system taking dependencies into account automatically
+and with no chance of doing mistakes, once the configuration is correctly
+set up.
+
+prev [manual](manual.html)
+next [buildid](buildid.html)
--- /dev/null
+buildid
+-------
+
+
+A buildid is calculated for each result that is built. The buildid,
+technically being a sha1 hash, identifies all configuration values
+that influence the build process. That key is used to store the
+result.
+
+That means, if the user requests building a result, *e2factory* may
+check if the result already exists and simply skip building it if
+it does. This feature offers the unique combination of being sure to
+build exactly all results that are influenced by changes done to the
+project configuration or sources.
+
+That makes the workflow as easy as just doing changes and requesting
+the tool to build the system (or a specific set of results). *e2factory*
+minimizes the time by rebuilding exactly the results that are influenced
+by the changes.
+
+The picture shows a quick overview of how the buildid is composed:
+
+
+
+prev [basic-concepts](basic-concepts.html)
+next [environment](environment.html)
--- /dev/null
+Chroot
+------
+
+
+The build environment, called chroot, is organzied in groups. A result
+specifies its chroot groups and the tool will setup the build environment
+using all files from the groups.
+
+The groups listed in the default_groups list are used with all results.
+
+The chroot groups are set up in the order they appear in the configuration
+file.
+
+To setup the chroot configuration call the e2chroot function with a
+table argument.
+
+The table must specify the following keys:
+
+* default_groups: list of strings
+* groups: list of groups specifications
+
+A group specification must specify the following keys:
+
+* name: group name
+* server: server name
+* files: list of file locations, relative to the server
+
+Example:
+
+ e2chroot {
+ default_groups = { "base", },
+ groups = {
+ { name = "base",
+ server = "chroot",
+ files = {
+ "chroot/20080916/base.tar.gz",
+ },
+ },
+ { name = "host-gcc",
+ server = "chroot",
+ files = {
+ chroot/20080916/binutils.tar.gz",
+ chroot/20080916/gcc34.tar.gz",
+ },
+ },
+ }
+ }
+
+prev [project](project.html)
+next [licences](licences.html)
--- /dev/null
+Environment
+-----------
+
+
+*e2factory* allows setting variables that are valid in the configuration as well
+as in the build processes.
+
+Variables can be published across the whole project or local to a set of
+results. In the first case the variables are available in the whole
+configuration and in all build processes. They influence all buildids.
+
+Publishing variables to a specific set of results will make the variables
+available only in the result configuration for those results and in the
+corresponding build process.
+
+To create a environment table in the configuration call the env function
+with a table argument. The table must have the following structure:
+
+* any key with a string value assigned is directly used as an environment
+ variable in the build process
+* if a key is assigned a table, the key is treated as a result name.
+ The table must hold keys with string values assigned. These key-value
+ pairs are usable in that results configuration and build process.
+
+Calling the env function with a string argument takes that string as a
+env file to load in addition to the current configuration file. The
+filename is relative to the project root.
+
+Examples:
+
+ env {
+ FOO = "bar",
+ ["busybox"] = { BUSYBOX_VERSION = "1.0" }
+ }
+
+ env "proj/more.env"
+
+prev [buildid](buildid.html)
+next [project](project.html)
--- /dev/null
+FAQ
+-----------------------------
+
+-----
+Q: e2-fetch-project does not work with e2-2.2.x. Why?
+
+A: The syntax changed. Call e2-fetch-project --help
+
+-----
+Q: Tool x does no longer work with e2-2.2.x
+
+A: The tool has disappeared, its syntax changed or the use-case is
+ no longer supported. Use the --help flag.
+
+-----
+prev [migration](migration.html)
+next [manual](manual.html)
--- /dev/null
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<style type="text/css">
+@import url(manual.css);
+</style>
+</head>
+<body>
+<img src="e2factory_logo.png" class="logo" />
+<h2>Users manual</h2>
+
--- /dev/null
+Licences
+--------
+
+
+*e2factory* allows configuring licences that may can be referenced in source
+configurations later on.
+
+To configure licences call the e2licence function with a table argument.
+The table must specify keys with table values. The keys are used as
+the licence identifier, that can be referenced in source configurations.
+The table assigns files to the licence:
+
+* server string: speficy the server name
+* files table: a list of files located on that server
+
+Example:
+
+ e2licence {
+ gpl_2 = {
+ server = "upstream",
+ files = {
+ "licence/gpl-2/COPYING",
+ }
+ }
+ }
+
+prev [chroot](chroot.html)
+next [sources](sources.html)
--- /dev/null
+/* manual.css -*- Java -*- */
+
+pre {
+ border-style: dashed;
+ border-color: black;
+ border-width: 1;
+ width: 70%;
+ position: relative;
+ left: 5%;
+ padding: 1em;
+ background-color: #eee;
+}
--- /dev/null
+Content
+-----------------
+
++ Concepts
+ * [basic-concepts](basic-concepts.html)
+ * [buildid](buildid.html)
+
++ Configuration
+
+ * [environment](environment.html)
+ * [project](project.html)
+ * [chroot](chroot.html)
+ * [licences](licences.html)
+ * [sources](sources.html)
+ * [results](results.html)
+
++ Tools
+ * e2-ls-project
+ * e2-build
+
++ Advanced topics
+ * [building releases](release.html)
+ * [migrate from e2-2.1 to e2-2.2.x](migration.html)
+
++ FAQ
+ * [FAQ](faq.html)
+
+next [basic-concepts](basic-concepts.html)
--- /dev/null
+Migrate from e2-2.1 to e2-2.2.x
+-----------------------------
+
+
+Migrating from e2-2.1 to e2-2.2.x requires the following steps:
+
+#### Prerequisites: Cleanup the project state: ####
+
+* all project members push valuable changes to sources or the project
+ repository to the server
+* all project members except the one doing the migration delete their
+ full project working copies.
+ They become unusable during the migration to e2-2.2.x.
+* be sure the project is in a state that builds without errors
+* tag the project repository
+* install e2-2.2.x global tools
+
+#### Migrate the project, 1st part ####
+
+* switch the project to use e2-2.2.x local tools:
+ Change .e2/e2version to hold 'e2-2.2 e2-2.2.0', commit and push
+* the old project checkout is no longer used now. Remove it.
+
+The commands:
+
+ $ echo 'e2-2.2 e2-2.2.0' >.e2/e2version
+ $ git commit -m 'switch the project to use e2-2.2.0' .e2/e2version
+ $ git push
+
+#### Migrate the project 2nd part ####
+
+* checkout the project again. That will install the e2-2.2.x local tools
+* run e2-build. It will complain about .e2/syntax not matching the local
+ tools.
+* Create the .e2/syntax file holding the syntax identifier string
+ mentioned by e2-build (Something like `2_2_x`).
+* Now the configuration syntax itself must be fixed to match the syntax
+ identifier.
+
+The commands:
+
+ $ e2-fetch-project ...
+ $ cd ...
+ $ e2-build
+ $ echo '2_2_0' >.e2/syntax
+ $ git add .e2/syntax
+
+#### Migrate the project 3rd part ####
+
+Some details of the project configuration change. Follow the steps below
+to change your project configuration to fit e2-2.2.x.
+Please `git add` all of your changed files immediately, to create a clean
+commit once the migration is successful.
+
+sed scripts listed in the *Useful Commands* section may help you doing the
+work.
+
+* create `proj/config`
+ + see [project](project.html)
+ + `proj/name` and `proj/default-results` become obsolete. Remove them.
+* fix the chroot configuration (proj/chroot)
+ + see [chroot](chroot.html)
+* fix the licence configuration (proj/licences)
+ + see [licences](licences.html)
+* fix the source configuration files (src/*/config)
+ + see [sources](sources.html)
+ + rename the `name` attribute to `location` for *files* sources
+ + the `server` attribute can now be specified once on the top-level
+ and is inherited by each single file, for *files* sources
+ + rename the `remote` attribute to `location` for git sources
+ + the `working` attribute for all scm sources has a builtin default
+ now: `in/<sourcename>`. No need to specify it any more.
+* fix the result configuration (res/*/config)
+ + see [results](results.html)
+ + the `files` attribute was removed. The tools will only warn, but
+ work anyway.
+
+#### Finish the migration ####
+
+* fetch sources and build with all warnings enabled
+* read the warnings and error messages
+
+The commands:
+
+ $ e2-fetch-sources
+ $ e2-build --Wall
+
+The basic migration is done now. Create the commit now and push:
+
+ $ git commit -m 'migrate to e2-2.2.0'
+ $ git push
+
+#### Useful commands ####
+
+Be careful when using these commands. They will do in many cases, but they
+are not reliable. They will even do nonsense, if the patterns match in the
+wrong places. Review the resulting diff before committing changes!
+
+ $ sed -i s,'group=','name=', proj/chroot
+ $ sed -i s,'group =','name =', proj/chroot
+ $ sed -i s,'name =','location =', src/*/config
+ $ sed -i s,'name=','location=', src/*/config
+ $ sed -i s,'remote =','location =', src/*/config
+ $ sed -i s,'remote=','location=', src/*/config
+
+prev [release](release.html)
+next [faq](faq.html)
--- /dev/null
+Project
+-------
+
+
+*e2factory* needs some configuration information to run, that is collected in the
+project configuration.
+
+To setup the project configuration call the e2project function with a
+table argument.
+
+The table must specify the following keys:
+
+* release_id string: release identifier
+* name string: project name
+* default_results table: list of results to build by default
+
+The table may specify the following additional keys:
+
+* chroot\_arch string: architecture to use when running the build process
+ x86\_64 or x86\_32 may be used, the latter is the builtin default.
+
+Example:
+
+ e2project {
+ name = "foo",
+ release_id = "foo-1.0beta",
+ default_results = { "foo" },
+ chroot_arch = "x86_32",
+ }
+
+prev [environment](environment.html)
+next [chroot](chroot.html)
--- /dev/null
+Building releases
+-----------------
+
+
+Prepare building a release with *e2factory*:
+
+* for each scm source:
+ * make sure the source is tagged and the tag is configured in the
+ source configuration
+* change the release_id attribute in proj/config to the release string
+* commit the file
+* push
+
+To make sure everything is really available from the server:
+
+* create a fresh checkout for the project
+* fetch sources
+* build in release mode
+* e2factory will
+ * build everything from scratch
+ * store the results to the releases server
+ * the location will be `releases:<project-location>/release/<release-id>/`
+
+Example:
+
+ $ e2-fetch-project project
+ $ cd project
+ $ e2-fetch-sources
+ $ e2-build --build-mode=release
+
+prev [results](results.html)
+next [migrate from e2-2.1 to e2-2.2](migration.html)
--- /dev/null
+Results
+-------
+
+
+To create a result in the configuration call the e2result function
+with a table argument. Usually a source configures one or more of the
+following keys:
+
+* sources list of sources
+* chroot list of chroot groups
+* depends list of dependencies
+
+To use the `collect_project` feature the following keys must be used
+in addition:
+
+* `collect_project` boolean: enable the collect_project flag for this result
+* `collect_project_default_result` string: name of the result that should
+ be reproducable
+
+In addition creating a `build-script` is required. The build script must
+be stored in `./res/<name>/build-script` and runs in the build environment.
+
+The `collect_project` flag provides a tree holding everything to rebuild
+the result configured in `collect_project_default_result` in $TMPDIR/project.
+The user must write a build-script to package that tree to a file in
+$TMPDIR/out
+
+All configuration options can be combined. And even a configuration with
+no keys is valid: The `base` chroot group is used automatically, and the
+`build-script` runs and may create a result.
+
+Example configuration:
+
+ e2result {
+ chroot = {
+ "autotools",
+ },
+ sources = {
+ "hello",
+ "hello-config",
+ },
+ depends = {
+ "toolchain",
+ },
+ }
+
+Example (using the `collect_project` flag)
+
+ e2result {
+ collect_project = true,
+ collect_project_default_result = "bsp",
+ sources = { },
+ depends = { },
+
+ }
+
+Example build script:
+
+ DESTDIR=$TMPDIR/root
+ make hello
+ make DESTDIR=$DESTDIR install
+ tar -C $DESTDIR -czf $TMPDIR/out/$RESULT.tar.gz .
+
+prev [sources](sources.html)
+next [release](release.html)
--- /dev/null
+Sources
+-------
+
+
+To create a source from the configuration call the e2source function,
+with a table argument. The table must use the following keys:
+
+generic configuration:
+
+* type: string
+* server: string
+* licences: (old: licence) table of strings
+
+files source (type = files, default)
+
+* file: file configuration table
+ * server string: server name (optional, inherited from top-level)
+ * licences table of strings (optional, inherited from top-level)
+ * location (old: name) string: file location
+ * unpack string: the directory included in the source package
+ * patch string: strip n directories when
+ * copy string: the directory to copy the file into, relative to
+
+Example:
+
+ e2source {
+ type = "files",
+ server = "upstream",
+ licences = { "gpl2" },
+ file = {
+ {
+ location = "z/zlib/1.2.3/zlib-1.2.3.tar.gz",
+ unpack = "zlib-1.2.3",
+ },
+ }
+ }
+
+cvs source (type = cvs)
+
+* server
+* cvsroot
+* module
+* working
+* branch
+* tag
+
+Example:
+
+ e2source {
+ type = "cvs",
+ licence = "gpl2",
+ server = "cvs-server",
+ cvsroot = ".",
+ module = "hello",
+ branch = "HEAD",
+ tag = "hello-1.0",
+ }
+
+svn source (type = svn)
+
+* server
+* location
+* branch
+* tag
+* workingcopy_subdir
+
+Example:
+
+ e2source {
+ type = "svn",
+ licence = "gpl2",
+ server = "svn-server",
+ location = "svn-repo",
+ branch = "trunk",
+ --branch = "branches/hello-1.0",
+ tag = "tags/hello-1.0",
+ --workingcopy_subdir = "trunk", -- defaults to the branch directory
+ }
+
+git source (type = git)
+
+* server
+* remote (rename to: location)
+* working
+* branch
+* tag table: list of tags, the last one is used for building
+
+Example:
+
+ e2source {
+ type = "git",
+ licence = "gpl2",
+ server = "git-server",
+ remote = "git/h/hello.git",
+ branch = "master",
+ tag = "hello-1.0",
+ }
+
+subversion source (type = svn)
+
+* ...
+
+prev [licences](licences.html)
+next [results](results.html)
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+### Makefile for "generic" e2 subdirectory
+
+
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+CLEAN_FILES = *~ *.lc *.so lua-version-map.lua e2-su-2.2
+
+
+.PHONY: all install uninstall local install-local clean
+
+all: e2generic_global.lc luafile_ll_global.so e2util_global.so e2-su-2.2
+
+install: all
+ install -m 644 e2generic_global.lc $(DESTDIR)$(LIBDIR)
+ install -m 755 luafile_ll_global.so $(DESTDIR)$(LIBDIR)
+ install -m 755 e2util_global.so $(DESTDIR)$(LIBDIR)
+ install -m 4754 -o root -g $(E2_GROUP) e2-su-2.2 $(DESTDIR)$(BINDIR)/
+
+uninstall:
+ rm -f $(DESTDIR)$(BINDIR)/e2-su-2.2
+ rm -f $(DESTDIR)$(LIBDIR)/e2generic_global.lc
+ rm -f $(DESTDIR)$(LIBDIR)/luafile_ll_global.so
+ rm -f $(DESTDIR)$(LIBDIR)/e2util_global.so
+
+local: e2generic_local.lc luafile_ll_local.so e2util_local.so
+
+install-local: local
+ mkdir -p $(LOCALLIBDIR) $(LOCALMAKDIR)
+ install -m 644 e2generic_local.lc $(LOCALLIBDIR)
+ install -m 755 luafile_ll_local.so $(LOCALLIBDIR)
+ install -m 755 e2util_local.so $(LOCALLIBDIR)
+ install -m 644 e2-su-2.2.c $(LOCALMAKDIR)
+
+doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+clean:
+ rm -f $(CLEAN_FILES)
+
+e2generic_global.lc: strict.lua collection.lua e2lib_global_prefix.lua \
+ e2lib.lua e2hook.lua e2option.lua sha1.lua \
+ transport.lua cache.lua url.lua scm.git.lua \
+ convenience.lua luafile.lua lua-version-map.lua \
+ error.lua
+ $(BUILD_LUAC) -o $@ $^
+
+e2generic_local.lc: strict.lua collection.lua e2lib_local_prefix.lua \
+ e2lib.lua e2hook.lua e2option.lua sha1.lua \
+ transport.lua cache.lua url.lua scm.git.lua \
+ convenience.lua luafile.lua lua-version-map.lua
+ $(LUAC) -o $@ $^
+
+%: %.in
+ $(TOPLEVEL)/scripts/genscript.sh $< $@
+
+e2-su-2.2: e2-su-2.2.c
+ $(CC) $(CFLAGS) $(E2_SU_CFLAGS) $(LDFLAGS) $< -o $@
+
+luafile_ll_global.so: luafile_ll.c
+ $(CC) $(CFLAGS) $(BUILD_LUA_CPPFLAGS) $(LDFLAGS) -shared -fPIC $< -o $@
+
+e2util_global.so: e2util.c
+ $(CC) $(CFLAGS) $(BUILD_LUA_CPPFLAGS) $(LDFLAGS) -shared -fPIC $< -o $@
+
+luafile_ll_local.so: luafile_ll.c
+ $(CC) -DLOCAL $(CFLAGS) $(LUA_CPPFLAGS) $(LDFLAGS) -shared -fPIC $< -o $@
+
+e2util_local.so: e2util.c
+ $(CC) -DLOCAL $(CFLAGS) $(LUA_CPPFLAGS) $(LDFLAGS) -shared -fPIC $< -o $@
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+--- cache
+-- @class table
+-- @name cache
+-- @field name a human readable name
+-- @field url cache base url
+-- @field ce cache entries
+
+--- cache entry
+-- @class table
+-- @name cache entry
+-- @field server the server name
+-- @field remote_url the remote server url
+-- @field cache_url the cache url (must be a file:/// url)
+-- @field flags default flags for this cache entry
+
+--- flags influencing the caching behaviour
+-- @class table
+-- @name flags
+-- @field cachable treat a server as cachable?
+-- @field refresh refresh a cached file?
+-- @field check_only check if a file is in the cache, without fetching
+
+--- create a new cache table
+-- @param name a cache name
+-- @param url base url for this cache, must use file transport
+-- @return a cache table
+function new_cache(name, url)
+ local debug = false
+ local c = {}
+ c.name = name
+ c.url = url
+ c.ce = {}
+ e2lib.log(4, "Cache: " .. c.name)
+ e2lib.log(4, " url: " .. c.url)
+ if debug then
+ for k,v in pairs(c) do
+ print(k,v)
+ end
+ end
+ return c
+end
+
+--- create a new cache entry
+-- @param cache a cache table
+-- @param server the remote server name
+-- @param remote_url the remote server to cache
+-- @return true on success, false on error
+-- @return an error object on failure
+function new_cache_entry(cache, server, remote_url, flags)
+ local ru, cu
+ local rc, re
+ local e = new_error("error setting up cache entry")
+ local ce = {}
+ ru, re = url.parse(remote_url)
+ if not ru then
+ return false, e:cat(re)
+ end
+ ce.server = server
+ ce.remote_url = ru.url
+ ce.cache_url = string.format("%s/%s", cache.url, server)
+ ce.flags = {}
+ ce.flags.cachable = flags.cachable
+ ce.flags.cache = flags.cache and flags.cachable
+ ce.flags.push_permissions = flags.push_permissions
+ if flags.islocal ~= nil then
+ ce.flags.islocal = flags.islocal
+ elseif ru.transport == "file" then
+ ce.flags.islocal = true
+ else
+ ce.flags.islocal = false
+ end
+ if flags.writeback ~= nil then
+ ce.flags.writeback = flags.writeback
+ end
+ ce.cache_url = string.format("%s/%s", cache.url, server)
+ if cache.ce[server] then
+ return false, e:append("cache entry for server exists")
+ end
+ cache.ce[server] = ce
+ e2lib.log(4, "cache entry: " .. ce.server .. " (" .. cache.name .. ")")
+ e2lib.log(4, " remote url: " .. ce.remote_url)
+ if ce.cache_url then
+ e2lib.log(4, " cache url: " .. ce.cache_url)
+ end
+ return true, nil
+end
+
+--- get cache entry by url
+-- @param cache the cache table
+-- @param url the server url
+-- @return the cache entry table, nil on error
+-- @return an error object on failure
+function ce_by_url(cache, url)
+ for _,ce in pairs(cache.ce) do
+ if ce.remote_url == url then
+ return ce, nil
+ end
+ end
+ return nil, new_error("no cache entry for url: %s", url)
+end
+
+--- get cache entry by server
+-- @param cache the cache table
+-- @param server the server name
+-- @return the cache entry table, nil on error
+-- @return an error object on failure
+function ce_by_server(cache, server)
+ for _,ce in pairs(cache.ce) do
+ if ce.server == server then
+ return ce, nil
+ end
+ end
+ return nil, new_error("no cache entry for server: %s", server)
+end
+
+--- get remote url
+-- for use in scm implementations where urls need to be handled manually
+-- @param cache the cache table
+-- @param server the server name
+-- @param location the location relative to the server
+-- @return the remote url, nil on error
+-- @return an error object on failure
+function remote_url(cache, server, location)
+ local ce, e = ce_by_server(cache, server)
+ if not ce then
+ return nil, e
+ end
+ local url = string.format("%s/%s", ce.remote_url, location)
+ return url
+end
+
+--- check if a cache is enabled
+-- @param cache a cache table
+-- @param server the server name
+-- @return bool
+-- @return an error object on failure
+function cache_enabled(c, server)
+ e2lib.log(4, "cache.file_in_cache(%s,%s,%s)", tostring(c),
+ tostring(server))
+ local ce, re = ce_by_server(c, server)
+ if not ce then
+ return false, re
+ end
+ return ce.flags.cache
+end
+
+--- check if a file is available in the cache
+-- @param cache a cache table
+-- @param server the server name
+-- @param location location relative to the server url
+-- @return bool
+-- @return an error object on failure
+function file_in_cache(c, server, location)
+ e2lib.logf(4, "cache.file_in_cache(%s,%s,%s)", tostring(c),
+ tostring(server), tostring(location))
+ local ce, re = ce_by_server(c, server)
+ if not ce then
+ return false, re
+ end
+ local ceurl, re = url.parse(ce.cache_url)
+ if not ceurl then
+ return false, re
+ end
+ local cf = string.format("/%s/%s", ceurl.path, location)
+ local rc, re = e2lib.isfile(cf)
+ if not rc then
+ return false
+ end
+ e2lib.log(4, "file is in cache")
+ return true
+end
+
+--- check if a file is available locally
+-- @param cache a cache table
+-- @param server the server name
+-- @param location location relative to the server url
+-- @return bool
+-- @return an error object on failure
+function file_local(c, server, location)
+ e2lib.logf(4, "file_local(%s,%s,%s)", tostring(c), tostring(server),
+ tostring(location))
+ local rc, re = file_in_cache(c, server, location)
+ if re then
+ return false, re
+ end
+ if rc then
+ return true, nil
+ end
+ local ce, re = ce_by_server(c, server)
+ if not ce then
+ return false, re
+ end
+ if ce.islocal == false then
+ return false
+ end
+ local path, re = file_path(c, server, location)
+ if re then
+ return false, re
+ end
+ if not path then
+ return false
+ end
+ if not e2lib.isfile(path) then
+ return false
+ end
+ e2lib.log(4, "file is on local server")
+ return true
+end
+
+--- fetch a file from a server, with caching in place
+-- @param cache a cache table
+-- @param server the server name
+-- @param location location relative to the server url
+-- @param destdir where to store the file locally
+-- @param destname filename of the fetched file
+-- @param flags table of flags
+-- @return bool
+-- @return an error object on failure
+function fetch_file(cache, server, location, destdir, destname, flags)
+ e2lib.log(4, string.format("%s: %s, %s, %s, %s, %s", "fetch_file()",
+ tostring(server), tostring(location), tostring(destdir),
+ tostring(destname), tostring(flags)))
+ local rc, re
+ local e = new_error("cache: fetching file failed")
+ local ce, re = ce_by_server(cache, server)
+ if not ce then
+ return false, e:cat(re)
+ end
+ if not destname then
+ destname = e2lib.basename(location)
+ end
+ -- fetch the file
+ if ce.flags.cache then
+ -- cache is enabled:
+ -- fetch from source to cache and from cache to destination
+ rc, re = cache_file(cache, server, location, flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = transport.fetch_file(ce.cache_url, location,
+ destdir, destname, flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ else
+ -- cache is disabled:
+ -- fetch from source to destination
+ rc, re = transport.fetch_file(ce.remote_url, location,
+ destdir, destname, flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ if flags.chmod then
+ local f = string.format("%s/%s", destdir, destname)
+ rc, re = e2lib.chmod(flags.chmod, f)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+end
+
+--- push a file to a server: cache and writeback
+-- @param cache a cache table
+-- @param sourcefile where to store the file locally
+-- @param server the server name
+-- @param location location relative to the server url
+-- @param flags table of flags
+-- @return bool
+-- @return an error object on failure
+function push_file(cache, sourcefile, server, location, flags)
+ local rc, re
+ local e = new_error("error pushing file to cache/server")
+ e2lib.log(4, string.format("%s: %s, %s, %s", "push_file()",
+ sourcefile, server, location))
+ local ce, re = ce_by_server(cache, server)
+ if not ce then
+ return false, e:cat(re)
+ end
+ if ce.flags.cache then
+ -- cache is enabled:
+ -- push the file from source to cache and from cache to
+ -- destination
+ rc, re = transport.push_file(sourcefile, ce.cache_url, location,
+ nil)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = writeback(cache, server, location, flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ else
+ -- cache is disabled
+ -- push the file from source to destination immediately
+ rc, re = transport.push_file(sourcefile, ce.remote_url,
+ location, ce.flags.push_permissions)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+end
+
+--- writeback a cached file
+-- @param cache the cache data structure
+-- @param server the server to fetch the file from
+-- @param location the location on the server
+-- @return bool
+-- @return an error object on failure
+function writeback(cache, server, location, flags)
+ e2lib.log(4, string.format("writeback(): %s %s %s", cache.name,
+ server, location))
+ local e = new_error("writeback failed")
+ local rc, re
+ local ce, re = ce_by_server(cache, server)
+ if not ce then
+ return false, e:cat(re)
+ end
+ local ceurl, re = url.parse(ce.cache_url)
+ if not ceurl then
+ return false, e:cat(re)
+ end
+ if flags.writeback == false or
+ (ce.flags.writeback == false and flags.writeback ~= true) then
+ return true, nil
+ end
+ local sourcefile = string.format("/%s/%s", ceurl.path, location)
+ rc, re = transport.push_file(sourcefile, ce.remote_url, location,
+ ce.flags.push_permissions)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+--- cache a file
+-- @param cache the cache data structure
+-- @param server the server to fetch the file from
+-- @param location the location on the server
+-- @return bool
+-- @return an error object on failure
+function cache_file(cache, server, location, flags)
+ e2lib.log(4, string.format("cache_file(): %s %s %s %s",
+ tostring(cache), tostring(server), tostring(location),
+ tostring(flags)))
+ local e = new_error("caching file failed")
+ local rc, re
+ local ce, re = ce_by_server(cache, server)
+ if not ce then
+ return false, e:cat(re)
+ end
+ if not ce.flags.cache then
+ return true, nil
+ end
+ local ceurl, re = url.parse(ce.cache_url)
+ if not ceurl then
+ return false, e:cat(re)
+ end
+ local avail, re = file_in_cache(cache, server, location)
+ if avail and flags.check_only then
+ -- file is in the cache and just checking was requested
+ return true, nil
+ end
+ if avail and not flags.refresh then
+ -- file is in the cache and no refresh requested
+ return true, nil
+ end
+ local destdir = string.format("/%s/%s", ceurl.path,
+ e2lib.dirname(location))
+ -- fetch the file to the cache
+ rc, re = transport.fetch_file(ce.remote_url, location, destdir, nil)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+--- get path to a cached file or a file on a local server
+-- The user must cache the file first using cache.cache_file()
+-- @param cache the cache data structure
+-- @param server the server where the file is located
+-- @param location the location on the server
+-- @return string the path to the cached file, nil on error
+-- @return an error object on failure
+function file_path(cache, server, location, flags)
+ e2lib.log(4, string.format("file_path(): %s %s %s",
+ cache.name, server, location))
+ local rc, re
+ local e = new_error("providing file path failed")
+ -- get the cache entry
+ local ce, re = ce_by_server(cache, server)
+ if not ce then
+ return nil, e:cat(re)
+ end
+ if ce.flags.cache then
+ -- cache enabled. cache the file and return path to cached
+ -- file
+ local path, re = transport.file_path(ce.cache_url, location)
+ if not path then
+ return nil, e:cat(re)
+ end
+ return path, nil
+ end
+ -- try if the transport delivers a path directly (works for file://)
+ local path, re = transport.file_path(ce.remote_url, location)
+ if not path then
+ e:append("Enable caching for this server.")
+ return nil, e:cat(re)
+ end
+ return path, nil
+end
+
+cache = {}
+cache.new_cache = new_cache
+cache.new_cache_entry = new_cache_entry
+cache.cache_file = cache_file
+cache.file_path = file_path
+cache.fetch_file = fetch_file
+cache.push_file = push_file
+cache.remote_url = remote_url
+cache.cache_enabled = cache_enabled
+cache.file_in_cache = file_in_cache
+cache.file_local = file_local
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- Topological sorting
+--
+-- table.tsort(DAG) -> ARRAY
+--
+-- Expects a table of tables as input, where each sub-table contains
+-- the node name followed by its dependencies, and returns a topologically
+-- sorted array of all entries.
+-- When DAG is cyclic, return nil
+
+function table.tsort(dag)
+ local sorted = {}
+ local adjust = {}
+ local colour = {}
+ local function visit(u, path)
+ local p = path
+ if p[u] then sorted = nil end
+ if sorted and not colour[u] then
+ local v = adjust[u]
+ colour[u] = true
+ if v then
+ p[u] = true
+ for i = 1, #v do visit(v[i], p) end
+ p[u] = nil
+ end
+ if sorted then table.insert(sorted, u) end
+ end
+ end
+ for i = 1, #dag do
+ local l = {}
+ for j = 2, #dag[i] do table.insert(l, dag[i][j]) end
+ adjust[dag[i][1]] = l
+ end
+ for i = 1, #dag do visit(dag[i][1], {}) end
+ return sorted
+end
+
+
+-- Table operations
+--
+-- table.reverse(TABLE) -> TABLE'
+--
+-- Reverse array elements and return new table.
+--
+-- table.map(TABLE, FUNCTION) -> TABLE'
+--
+-- Map over table elements creating new table. FUNCTION is called for each
+-- table value and the result will be stored under the same key as the original
+-- value.
+--
+-- table.filter(TABLE, FUNCTION) -> TABLE'
+--
+-- Returns a new table with all elements from TABLE removed that do not
+-- satisfy the predicate FUNCTION.
+--
+--
+-- table.grep(TABLE, PATTERN) -> TABLE'
+--
+-- Filter strings matching a pattern.
+--
+-- table.print(TABLE, [OUT])
+--
+-- Prints table contents on OUT, which defaults to io.stdout.
+--
+-- table.compare(TABLE1, TABLE2, [FUNCTION]) -> BOOL
+--
+-- Compares tables element by element by passing each pair of elements
+-- to FUNCTION (which defaults to "function(x, y) return x == y end").
+--
+-- table.find(TABLE, VAL, [FUNCTION]) -> KEY, VAL
+--
+-- Searches TABLE for an entry VAL and returns the key (and value). FUNCTION
+-- is the comparison function used to compare the values and defaults to
+-- "function(x, y) return x == y end".
+
+function table.reverse(t)
+ local t2 = {}
+ local len = #t
+ local j = 1
+ for i = len, 1, -1 do
+ t2[ j ] = t[ i ]
+ j = j + 1
+ end
+ return t2
+end
+
+function table.map(t, f)
+ local t2 = {}
+ for k, x in pairs(t) do
+ t2[ k ] = f(x)
+ end
+ return t2
+end
+
+function table.filter(t, f)
+ local t2 = {}
+ local i = 1
+ for k, x in pairs(t) do
+ if f(x) then
+ t2[ i ] = x
+ i = i + 1
+ end
+ end
+ return t2
+end
+
+function table.grep(t, p)
+ local function match(x)
+ return string.find(x, p)
+ end
+ return table.filter(t, match)
+end
+
+function table.print(t, out)
+ local out = out or io.stdout
+ out:write(tostring(t), ":\n")
+ for k, v in pairs(t) do
+ print("", k, "->", v)
+ end
+end
+
+function table.compare(t1, t2, p)
+ local p = p or function(x, y) return x == y end
+ if #t1 ~= #t2 then return false
+ else
+ for k, v in pairs(t1) do
+ local x = t2[ k ]
+ if not p(v, x) then return false end
+ end
+ return true
+ end
+end
+
+function table.find(t, x, cmp)
+ cmp = cmp or function(x, y) return x == y end
+ for k, v in pairs(t) do
+ if cmp(v, x) then return k, v end
+ end
+ return nil
+end
+
+
+-- String operations
+--
+-- string.trim(STRING) -> STRING'
+--
+-- Removes whitespace on both sides from string.
+--
+-- string.explode(STRING) -> ARRAY
+--
+-- Convert string into array of characters (one-element strings).
+--
+-- string.split(STRING, PATTERN) -> ARRAY
+--
+-- Split string into elements matching PATTERN.
+
+function string.trim(str)
+ return string.match(str, "^%s*(.*%S)%s*$") or ""
+end
+
+function string.explode(str)
+ local t = {}
+ for i = 1, #str do
+ table.insert(t, string.sub(str, i, i))
+ end
+ return t
+end
+
+function string.split(str, pat)
+ local t = {}
+ pat = pat or "%S+"
+ for x in string.gmatch(str, pat) do
+ table.insert(t, x)
+ end
+ return t
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- print table (see collection.lua)
+
+function pt(t)
+ table.foreach(t, print)
+end
+
+-- shortcuts
+
+p = print
+e = e2lib.bomb
+
+info = false
+
+function getinfo()
+ info = e2tool.collect_project_info()
+ p("project info collected in `info'")
+ return info
+end
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <limits.h>
+#include <grp.h>
+#include <libgen.h>
+
+/* #define DEBUG 1 */
+
+#ifndef CHROOT_TOOL
+#error CHROOT_TOOL is not set
+#endif
+
+#ifndef TAR_TOOL
+#error TAR_TOOL is not defined
+#endif
+
+#ifndef CHOWN_TOOL
+#error CHOWN_TOOL is not defined
+#endif
+
+#ifndef RM_TOOL
+#error RM_TOOL is not defined
+#endif
+
+char *chroot_tool = CHROOT_TOOL;
+char *tar_tool = TAR_TOOL;
+char *chown_tool = CHOWN_TOOL;
+char *rm_tool = RM_TOOL;
+
+void setuid_root()
+{
+ int rc;
+ rc = clearenv();
+ if(rc != 0) {
+ perror("can't clearenv()");
+ exit(99);
+ }
+ rc = setuid(0);
+ if(rc != 0) {
+ perror("can't setuid(0)");
+ exit(99);
+ }
+ rc = setgid(0);
+ if(rc != 0) {
+ perror("can't setgid(0)");
+ exit(99);
+ }
+ rc = setgroups(0, NULL);
+ if(rc != 0) {
+ perror("can't setgroups()");
+ exit(99);
+ }
+}
+
+void perr(char *msg)
+{
+ puts(msg);
+ exit(99);
+}
+
+void print_arg(char *arg[])
+{
+#ifdef DEBUG
+ int i;
+ for(i=0; arg[i]; i++) {
+ printf("%s\n", arg[i]);
+ }
+#endif
+}
+
+void assert_chroot_environment(char *path)
+{
+ char name[PATH_MAX];
+ snprintf(name, sizeof(name), "%s/emlix-chroot", path);
+ name[sizeof(name)-1]=0;
+ if(access(name, R_OK)) {
+ perr("not a chroot environment");
+ }
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ if(argc < 3) {
+ perr("too few arguments");
+ }
+ if(argc > 128) {
+ perr("too many arguments");
+ }
+ char *cmd = argv[1];
+ char *path = argv[2];
+ assert_chroot_environment(path);
+ if(!strcmp(cmd, "chroot_2_2")) {
+ /* chroot_2_2 <path> ... */
+ int i;
+ char *arg[256];
+ if(argc < 3) {
+ perr("too few arguments");
+ }
+ arg[0] = basename(chroot_tool);
+ arg[1] = path;
+ for (i=3; i < argc; i++) {
+ arg[i-1] = argv[i];
+ }
+ arg[i-1] = 0;
+ print_arg(arg);
+ setuid_root();
+ rc = execv(chroot_tool, arg);
+ perror("can't exec");
+ exit(99);
+ } else if(!strcmp(cmd, "extract_tar_2_2")) {
+ /* extract_tar_2_2 <path> <tartype> <file> */
+ char *arg[256];
+ if(argc != 5) {
+ perr("wrong number of arguments");
+ }
+ char *tartype = argv[3];
+ char *file = argv[4];
+ char *tararg = NULL;
+ if(!strcmp(tartype, "tar.gz")) {
+ tararg = "-xzf";
+ } else if(!strcmp(tartype, "tar.bz2")) {
+ tararg = "-xjf";
+ } else if(!strcmp(tartype, "tar")) {
+ tararg = "-xf";
+ } else {
+ perr("wrong tararg argument");
+ }
+ arg[0] = basename(tar_tool);
+ arg[1] = "-C";
+ arg[2] = path;
+ arg[3] = tararg;
+ arg[4] = file;
+ arg[5] = NULL;
+ print_arg(arg);
+ setuid_root();
+ rc = execv(tar_tool, arg);
+ perror("can't exec");
+ exit(99);
+ } else if(!strcmp(cmd, "set_permissions_2_2")) {
+ /* set_permissions_2_2 <path> */
+ char *arg[256];
+ if(argc != 3) {
+ perr("wrong number of arguments");
+ }
+ arg[0] = basename(chown_tool);
+ arg[1] = "root:root";
+ arg[2] = path;
+ arg[3] = NULL;
+ print_arg(arg);
+ setuid_root();
+ rc = execv(chown_tool, arg);
+ perror("can't exec");
+ exit(99);
+ } else if(!strcmp(cmd, "remove_chroot_2_2")) {
+ /* remove_chroot_2_2 <path> */
+ char *arg[256];
+ if(argc != 3) {
+ perr("wrong number of arguments");
+ }
+ arg[0] = basename(rm_tool);
+ arg[1] = "-r";
+ arg[2] = "-f";
+ arg[3] = path;
+ arg[4] = NULL;
+ print_arg(arg);
+ setuid_root();
+ rc = execv(rm_tool, arg);
+ perror("can't exec");
+ exit(99);
+ }
+ perr("unknown command");
+ exit(99);
+}
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+e2hook = e2lib.module("e2hook")
+
+e2hook.hooks = {}
+
+local hooks = {
+ "tool-start",
+ "tool-finish",
+ "pre-build",
+ "post-build",
+ "create-project",
+ "fetch-project",
+ "fetch-sources",
+ "enter-playground",
+ "use-source",
+ "build-setup-chroot",
+ "build-pre-runbuild",
+ "build-post-runbuild",
+ "build-remove-chroot",
+ "build-pre-sources",
+ "build-post-sources",
+ "build-pre-result",
+ "build-post-result",
+ "files-prepare-source",
+ "build-failure",
+}
+
+for _, k in pairs(hooks) do
+ e2hook.hooks[ k ] = true
+end
+
+e2hook.info = nil
+e2hook.arguments = nil
+
+function e2hook.log(msg)
+ e2lib.log(3, "[hook: " .. e2hook.hookname .. "] " .. msg)
+end
+
+function e2hook.run_hook(info, hookname, arguments, toolname)
+ if not e2hook.hooks[ hookname ] then
+ e2lib.bomb("invalid hook: ", hookname)
+ end
+ local hfile
+ if info then
+ hfile = info.root .. "/proj/hooks/" .. hookname
+ else
+ hfile = E2_PREFIX .. "/share/e2/hooks/" .. hookname
+ end
+ if e2util.exists(hfile) then
+ e2lib.log(3, "running hook `" .. hookname .. "' ...")
+ e2hook.arguments = arguments
+ e2hook.hookname = hookname
+ e2hook.info = info or {}
+ e2hook.toolname = toolname
+ dofile(hfile)
+ end
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+_version = "e2factory, the emlix embedded build system, version " ..
+ E2_VERSION
+
+_licence = [[
+e2factory is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.]]
+
+-- Module-level global variables
+--
+-- e2lib.interactive -> BOOL
+--
+-- True, when lua was started in interactive mode (either by giving
+-- the "-i" option or by starting lua and loading the e2 files
+-- manually).
+
+e2lib = {
+ logflags = {
+ { "v1", true }, -- minimal
+ { "v2", true }, -- verbose
+ { "v3", false }, -- verbose-build
+ { "v4", false } -- tooldebug
+ },
+ log_debug = false,
+ debug = false,
+ playground = false,
+ interactive = arg and (arg[ -1 ] == "-i"),
+ -- variables initialized in e2lib.init()
+ username = nil,
+ homedir = nil,
+ hostname = nil,
+ termwidth = 72,
+ env = {},
+ last_output = false,
+ abort_hook = false,
+ finish_hook = false,
+ tmpdirs = {},
+ tmpfiles = {},
+ enable_invocation_log = false,
+ default_projects_server = "projects",
+ default_project_version = "2",
+ -- where to fetch e2 in e2-install-e2
+ default_e2 = {
+ local_branch = "master",
+ local_tag = "^",
+ local_syntax = E2_SYNTAX,
+ },
+ local_e2_branch = nil,
+ local_e2_tag = nil,
+ --- command line arguments that influence global settings are stored here
+ -- @class table
+ -- @name cmdline
+ cmdline = {},
+ git_skip_checkout = true,
+ buildnumber_server_url = nil,
+ template_path = "/etc/e2/templates",
+}
+
+-- Interrupt handling
+--
+-- e2util sets up a SIGINT handler that calls back into this function.
+
+function e2lib.interrupt_hook()
+ e2lib.abort("*interrupted by user*")
+end
+
+
+-- Extended error checking for module bindings
+--
+-- e2lib.module(NAME, [TABLE]) -> TABLE
+--
+-- Manipulates the meta-table of TABLE to signal an error, when a
+-- non-existant field of it is accessed. Also sets the "name" entry for
+-- TABLE to the given argument. If no table argument is given, a new
+-- table is returned.
+
+function e2lib.module(name, t)
+ t = t or {}
+ t.name = name
+ local mt = {}
+ function mt.__index(t, k)
+ error("access to non-existant module binding `" .. k .. "' in `" .. t.name .. "'")
+ end
+ setmetatable(t, mt)
+ return t
+end
+
+--- make sure the environment variables inside the e2lib table are
+-- initialized properly, and abort otherwise
+-- This function always succeeds or aborts.
+function e2lib.init()
+ e2lib.log(4, "e2lib.init()")
+
+ e2lib.warn_category = {
+ WDEFAULT = false,
+ WDEPRECATED = false,
+ WOTHER = true,
+ WPOLICY = false,
+ WHINT = false,
+ }
+
+ -- get environment variables
+ local getenv = {
+ { name = "HOME", required = true },
+ { name = "USER", required = true },
+ { name = "TERM", required = false, default = "linux" },
+ { name = "E2_CONFIG", required = false },
+ { name = "TMPDIR", required = false, default = "/tmp" },
+ { name = "E2TMPDIR", required = false },
+ { name = "COLUMNS", required = false, default = "72" },
+ { name = "E2_SSH", required = false },
+ { name = "E2_LOCAL_BRANCH", required = false },
+ { name = "E2_LOCAL_TAG", required = false },
+ }
+
+ local osenv = {}
+ for _, var in pairs(getenv) do
+ var.val = os.getenv(var.name)
+ if var.required and not var.val then
+ e2lib.abort(string.format("%s is not set in the environment", var.name))
+ end
+ if var.default and not var.val then
+ var.val = var.default
+ end
+ osenv[var.name] = var.val
+ end
+ e2lib.osenv = osenv
+
+ -- assign some frequently used environment variables
+ e2lib.homedir = e2lib.osenv["HOME"]
+ e2lib.username = e2lib.osenv["USER"]
+ e2lib.terminal = e2lib.osenv["TERM"]
+ if e2lib.osenv["E2TMPDIR"] then
+ e2lib.tmpdir = e2lib.osenv["E2TMPDIR"]
+ else
+ e2lib.tmpdir = e2lib.osenv["TMPDIR"]
+ end
+
+ -- get the host name
+ e2lib.hostname = e2lib.program_output("hostname")
+ if not e2lib.hostname then
+ e2lib.abort("hostname ist not set")
+ end
+end
+
+function e2lib.init2()
+ local rc, re
+ local e = new_error("initializing e2lib (step2)")
+
+ -- get the global configuration
+ local config = e2lib.get_global_config()
+
+ -- honour tool customizations from the config file
+ if config.tools then
+ for k,v in pairs(config.tools) do
+ transport.set_tool(k, v.name, v.flags)
+ end
+ end
+
+ -- handle E2_SSH environment setting
+ local ssh = nil
+ ssh = e2lib.osenv["E2_SSH"]
+ if ssh then
+ e2lib.log(3, string.format(
+ "using ssh command from the E2_SSH environment variable: %s", ssh))
+ transport.set_tool("ssh", ssh)
+ end
+
+ -- initialize the transport library after resetting tools
+ local rc, re = transport.init()
+ if not rc then
+ e:cat(re)
+ end
+
+ -- get host system architecture
+ e2lib.host_system_arch, re = e2lib.get_sys_arch()
+ if not e2lib.host_system_arch then
+ e2lib.abort(e:cat(re))
+ end
+end
+
+--- return the output of a program, abort if the call fails
+-- @param cmd string: the program to call
+-- @return string: the program output
+function e2lib.program_output(cmd)
+ local i = io.popen(cmd)
+ if not i then
+ e2lib.abort("invocation of program failed: ", cmd)
+ else
+ local input = i:read("*a")
+ return input
+ end
+end
+
+--- print a warning, composed by concatenating all arguments to a string
+-- @param ... any number of strings
+-- @return nil
+function e2lib.warn(category, ...)
+ local msg = table.concat({...})
+ return e2lib.warnf(category, "%s", msg)
+end
+
+--- print a warning
+-- @param format string: a format string
+-- @param ... arguments required for the format string
+-- @return nil
+function e2lib.warnf(category, format, ...)
+ if (format:len() == 0) or (not format) then
+ e2lib.bomb("calling e2lib.warnf() with zero length format")
+ end
+ if type(e2lib.warn_category[category]) ~= "boolean" then
+ e2lib.bomb("calling e2lib.warnf() with invalid warning category")
+ end
+ if e2lib.warn_category[category] == true then
+ local prefix = "Warning: "
+ if e2lib.log_debug then
+ prefix = string.format("Warning [%s]: ", category)
+ end
+ e2lib.log(1, prefix .. string.format(format, ...))
+ end
+ return nil
+end
+
+--- exit, cleaning up temporary files and directories.
+-- Return code is '1' and cannot be overrided.
+-- This function takes any number of strings or an error object as arguments.
+-- Please pass error objects to this function in the future.
+-- @param ... an error object, or any number of strings
+-- @return This function does not return
+function e2lib.abort(...)
+ local t = { ... }
+ local e = t[1]
+ if e and e.print then
+ if e2lib.abort_hook then
+ e2lib.abort_hook("no message")
+ end
+ e:print()
+ else
+ local msg = table.concat(t)
+ if msg:len() == 0 then
+ e2lib.bomb("calling e2lib.abort() with zero length message")
+ end
+ if e2lib.abort_hook then
+ e2lib.abort_hook(msg)
+ end
+ e2lib.log(1, "Error: " .. msg)
+ end
+ e2lib.rmtempdirs()
+ e2lib.rmtempfiles()
+ os.exit(1)
+end
+
+--- write a message about an internal error, including a traceback
+-- and exit. Return code is 32.
+-- @param ... any number of strings
+-- @return This function does not return
+function e2lib.bomb(...)
+ local msg = table.concat({...})
+ io.stderr:write(
+ "Internal Error:\n" ..
+ msg .. "\n" ..
+ "\n" ..
+ "You encountered an internal error in the e2 tool.\n" ..
+ "Please send a description of the problem, including the\n" ..
+ "stacktrace below to <bugs@e2factory.org>.\n" ..
+ "If possible include a copy of the project in the bug report.\n" ..
+ "\n" ..
+ "Thank you - the e2factory team.\n")
+ io.stderr:write(debug.traceback().."\n")
+ os.exit(32)
+end
+
+function e2lib.sete2config(file)
+ e2util.setenv("E2_CONFIG", file, 1)
+ e2lib.osenv["E2_CONFIG"] = file
+ e2lib.cmdline["e2-config"] = file
+end
+
+--- enable or disable logging for level.
+-- @param level number: loglevel
+-- @param value bool
+-- @return nil
+function e2lib.setlog(level, value)
+ e2lib.logflags[level][2] = value
+end
+
+--- get logging setting for level
+-- @param level number: loglevel
+-- @return bool
+function e2lib.getlog(level)
+ return e2lib.logflags[level][2]
+end
+
+--- return highest loglevel that is enabled
+-- @return number
+function e2lib.maxloglevel()
+ local level = 0
+ for i = 1, 4 do
+ if e2lib.getlog(i) then level = i end
+ end
+ return level
+end
+
+--- get log flags for calling subtools with the same log settings
+-- @return string: a string holding command line flags
+function e2lib.getlogflags()
+ local logflags = ""
+ if e2lib.getlog(1) then
+ logflags = "--v1"
+ end
+ if e2lib.getlog(2) then
+ logflags = logflags .. " --v2"
+ end
+ if e2lib.getlog(3) then
+ logflags = logflags .. " --v3"
+ end
+ if e2lib.getlog(4) then
+ logflags = logflags .. " --v4"
+ end
+ if e2lib.log_debug then
+ logflags = logflags .. " --log-debug"
+ end
+ return " " .. logflags
+end
+
+--- log to the debug logfile, and log to console if e2lib.getlog(level)
+-- @param level number: loglevel
+-- @param format string: format string
+-- @param ... additional parameters to pass to string.format
+-- @return nil
+function e2lib.logf(level, format, ...)
+ if not format then
+ e2lib.bomb("calling e2lib.log() without format string")
+ end
+ local msg = string.format(format, ...)
+ return e2lib.log(level, msg)
+end
+
+--- log to the debug logfile, and log to console if e2lib.getlog(level)
+-- is true
+-- @param level number: loglevel
+-- @param msg string: log message
+-- @param ... strings: arguments required for the format string
+-- @return nil
+function e2lib.log(level, msg)
+ if level < 1 or level > 4 then
+ e2lib.bomb("invalid log level")
+ end
+ if not msg then
+ e2lib.bomb("calling e2lib.log() without log message")
+ end
+ local log_prefix = "[" .. level .. "] "
+ -- remove end of line if it exists
+ if msg:match("\n$") then
+ msg = msg:sub(1, msg:len() - 1)
+ end
+ -- always write log level to debug logfile
+ e2lib.debuglogfile:write(log_prefix .. msg)
+ e2lib.debuglogfile:write("\n")
+ e2lib.debuglogfile:flush()
+ if e2lib.getlog(level) then
+ if e2lib.log_debug then
+ io.stderr:write(log_prefix)
+ end
+ io.stderr:write(msg)
+ io.stderr:write("\n")
+ end
+ return nil
+end
+
+local buildidlogfile = false
+
+function e2lib.buildidlog(info, text)
+ if info then
+ if not buildidlogfile then
+ e2lib.mkdir(info.root .. "/log", "-p")
+ buildidlogfile = io.open(info.root .. "/log/buildid.log", "a+")
+ end
+ buildidlogfile:write(text .. "\n")
+ else
+ buildidlogfile:write(" time: ", os.time(), "\n",
+ "------------------------------------------\n")
+ buildidlogfile:close()
+ buildidlogfile = false
+ end
+end
+
+--- append a line to a file
+-- @param file string: filename
+-- @param line string: a line to append to the file
+-- @return bool
+-- @return nil, an error string on error
+local function append_to_file(file, line)
+ local f, msg = io.open(file, "a+")
+ if not f then
+ return false, msg
+ end
+ f:write(line)
+ f:close()
+ return true, nil
+end
+
+--- log tool invocations
+-- this function always succeeds or aborts
+-- @param info
+-- @param args
+-- @return nothing
+function e2lib.log_invocation(info, args)
+ local pname = "<none>"
+ if info then
+ pname = info.name
+ end
+ local logstring = string.format(
+ "%s %s %s/%s %s \"%s %s\"\n",
+ pname, os.date(), E2_VERSION, E2_COMMIT, e2lib.username,
+ arg[0], table.concat(args, " "))
+
+ -- always log to the user logfile
+ local ulogdir = string.format("%s/.e2", e2lib.homedir)
+ local ulogfile = string.format("%s/run.log", ulogdir)
+ e2lib.mkdir(ulogdir, "-p")
+ local rc, e = append_to_file(ulogfile, logstring)
+ if not rc then
+ e2lib.abort(string.format("can't log to file %s: %s", logfile, e))
+ end
+
+ -- log to the project logfile
+ if info then
+ local plogdir = string.format("%s/.e2", info.root)
+ local plogfile = string.format("%s/run.log", plogdir)
+ e2lib.mkdir(plogdir, "-p")
+ rc, e = append_to_file(plogfile, logstring)
+ if not rc then
+ e2lib.abort(string.format("can't log to file %s: %s", logfile, e))
+ end
+ end
+
+ -- send the queued logs from the user logfile to the server
+ local args = string.format(
+ "--silent --fail -0 -q -f " ..
+ "--header 'Content-type: text/plain' " ..
+ "--data-binary '@%s' --connect-timeout 1 " ..
+ "'http://e2data:6532/store-run-log.lua?project=%s&user=%s&host=%s' " ..
+ ">/dev/null",
+ ulogfile, pname, e2lib.username, e2lib.hostname)
+ local rc = true
+ local re = nil
+ if e2lib.enable_invocation_log == true then
+ -- really send logs only if enabled
+ rc, re = e2lib.curl(args)
+ end
+ if not rc then
+ local ulogfile_backup = ulogfile .. ".backup"
+ local args = string.format("'%s' >>'%s'", ulogfile, ulogfile_backup)
+ e2lib.cat(args)
+ e2lib.rm(ulogfile, "-f")
+ else
+ e2lib.log(3, "failed sending queued logs to the server")
+ end
+end
+
+--- exit from the tool, cleaning up temporary files and directories and
+-- running the finish_hook before.
+-- @param rc number: return code (optional, defaults to 0)
+-- @return This function does not return.
+function e2lib.finish(rc)
+ if not rc then
+ rc = 0
+ end
+ if e2lib.finish_hook then
+ e2lib.finish_hook(rc)
+ end
+ e2lib.rmtempdirs()
+ e2lib.rmtempfiles()
+ os.exit(rc)
+end
+
+
+-- Pathname operations
+--
+-- e2lib.dirname(PATH) -> STRING
+--
+-- Returns the directory part of the string PATH.
+--
+-- e2lib.basename(PATH, [EXTENSION]) -> STRING
+--
+-- Returns the filename part of PATH by stripping the directory part.
+-- if EXTENSION is given and if it matches the file-extension of PATH,
+-- then the extension part is also removed.
+--
+-- e2lib.splitpath(PATH) -> DIR, BASE, TYPE
+--
+-- Checks PATH for trailing "/" for a directory,
+-- splits up the real path into dir and base to ensure that
+-- DIR .. BASE will address the file, as DIR always ends in "/"
+-- TYPE is set to stat.type. return nil for non existing file
+--
+
+function e2lib.dirname(path)
+ local s, e, dir = string.find(path, "^(.*)/[^/]*$")
+ if dir == "" then return "/"
+ else return dir or "." end
+end
+
+function e2lib.basename(path, ext)
+ local s, e, base = string.find(path, "^.*/([^/]+)[/]?$")
+ if not base then base = path end
+ if ext then
+ if string.sub(base, -#ext) == ext then
+ return string.sub(base, 1, -#ext - 1)
+ end
+ end
+ return base
+end
+
+function e2lib.splitpath(path)
+ local p = e2util.realpath(path)
+ if not p then return nil, "path does not exist" end
+ local st = e2util.stat(p)
+ local sf = string.sub(path, -1) ~= "/"
+ if (st.type == "directory") == sf then
+ return nil, "is " .. (sf and "" or "not ") .. "a directory"
+ end
+ local s, e, d, b = string.find(p, "^(.*/)([^/]*)$")
+ return d, b == "" and "." or b, st.type
+end
+
+function e2lib.is_backup_file(path)
+ return string.find(path, "~$") or string.find(path, "^#.*#$")
+end
+
+function e2lib.chomp(str, chr)
+ local chr = chr or "/"
+ if string.sub(str, -1, -1) == chr then
+ return string.sub(str, 1, -2)
+ else
+ return str
+ end
+end
+
+-- determines the type of an archive
+-- say "z" for gzip, "j" for bzip2, "" for tar archive
+-- nil is returned for unknown data
+function e2lib.tartype(path)
+ local f, e = io.open(path, "r")
+ if not f then
+ e2lib.abort(e)
+ end
+ local d = f and f:read(512)
+ local l = d and string.len(d) or 0
+ local c = nil
+ f:close()
+ if l > 261 and string.sub(d, 258, 262) == "ustar" then c = ""
+ elseif l > 1 and string.sub(d, 1, 2) == "\031\139" then c = "z"
+ elseif l > 2 and string.sub(d, 1, 3) == "BZh" then c = "j"
+ elseif l > 3 and string.sub(d, 1, 4) == "PK\003\004" then c = "zip"
+ end
+ return c
+end
+
+-- generates a command to unpack an archive file
+-- physpath is the current location and filename to be unpacked later
+-- virtpath is the location and name of the file at the time of unpacking
+-- destdir is the path to where the unpacked files shall be put
+-- return unix command on success, nil otherwise
+function e2lib.howtounpack(physpath, virtpath, destdir)
+ local c = e2lib.tartype(physpath)
+ if c == "zip" then
+ c = "unzip \"" .. virtpath .. "\" -d \"" .. destdir .. "\""
+ elseif c then
+ c = "tar x" .. c .. "f \"" .. virtpath .. "\" -C \"" .. destdir .. "\""
+ end
+ return c
+end
+
+
+-- Unit test support
+--
+-- e2lib.testsuite([NAME])
+--
+-- With an argument: starts a suite of tests. Without an argument: finishes
+-- a testsuite. NAME should be a string describing the testsuite. All
+-- invocations of e2lib.check() inside a pair of e2lib.testsuite() calls
+-- are grouped together.
+--
+-- e2lib.check(RESULT, EXPECTED, [NAME])
+--
+-- Compares RESULT with EXPECTED and records whether both are structurally
+-- equal (using table.compare if needed). NAME can be given for diagnostic
+-- output.
+--
+-- See e2-run-tests for an example of using this facility.
+
+local testcount = 0
+
+function e2lib.testsuite(name)
+ if name then
+ testcount = 1
+ io.stderr:write("--- starting testsuite: \t", name, "\n")
+ e2lib.current_testsuite = name
+ e2lib.succeeded_tests = 0
+ e2lib.failed_tests = 0
+ elseif e2lib.current_testsuite then
+ io.stderr:write("--- finishing testsuite:\t", e2lib.current_testsuite, "\n")
+ io.stderr:write(" tests succeeded:\t", e2lib.succeeded_tests, "\n")
+ io.stderr:write(" failed:\t", e2lib.failed_tests, "\n")
+ if e2lib.failed_tests == 0 then
+ io.stderr:write(" ALL TESTS SUCCEEDED.\n")
+ else
+ io.stderr:write(" SOME TESTS FAILED.\n")
+ end
+ else error("no testsuite active") end
+end
+
+function e2lib.check(result, expected, name)
+ if name then io.write(name, " ...")
+ else io.write("test #", testcount, " ...") end
+ io.flush()
+ testcount = testcount + 1
+ local f
+ if type(expected) == "table" then
+ if type(result) == "table" then
+ f = table.compare(result, expected)
+ else
+ f = false
+ end
+ else f = expected == result end
+ if f then
+ e2lib.succeeded_tests = (e2lib.succeeded_tests or 0) + 1
+ print("\tok")
+ else
+ e2lib.failed_tests = (e2lib.failed_tests or 0) + 1
+ io.write("\tfailed - expected: ", expected, " but got: ", result, "\n")
+ end
+end
+
+
+-- Input/Output operations
+--
+-- e2lib.read_line(PATHNAME)
+--
+-- Reads a single line from the given file and returns it.
+--
+-- e2lib.read_all(FD, [BLOCKSIZE]) -> STRING
+--
+-- Reads all remaining input from a given file-descriptor. BLOCKSIZE
+-- specifies the size of subsequently read blocks and defaults to 1024.
+
+function e2lib.read_line(path)
+ local f, msg = io.open(path)
+ if not f then
+ return nil, new_error("%s", msg)
+ end
+ local l, msg = f:read("*l")
+ if not l then
+ return nil, new_error("%s", msg)
+ end
+ f:close()
+ return l
+end
+
+function e2lib.read_all(fd, blocksize)
+ local input = {}
+ local blocksize = blocksize or 1024
+ while true do
+ local s, msg = e2util.read(fd, blocksize)
+ if not s then e2lib.bomb("read error: ", msg)
+ elseif #s == 0 then break
+ else table.insert(input, s) end
+ end
+ return table.concat(input)
+end
+
+
+-- Iterators
+--
+-- These iterators are convenience functions for use in "for" statements.
+--
+-- e2lib.read_configuration(PATH)
+--
+-- Returns the successive non-empty lines contained in the file PATH.
+-- Comments (of the form "# ...") are removed.
+--
+-- e2lib.directory(PATH, [DOTFILES, [NOERROR]])
+--
+-- Successively returns the files in the directory designated by
+-- PATH. If DOTFILES is given and true, then files beginning with "."
+-- are also included in the listing.
+
+function e2lib.read_configuration(p)
+ if e2util.exists(p) then
+ local function nextline(s)
+ while true do
+ local ln = s:read("*l")
+ if not ln then
+ s:close()
+ return nil
+ elseif not string.find(ln, "^%s*#") and string.find(ln, "%S") then
+ local s = string.find(ln, "#.*")
+ if s then return string.sub(ln, 1, s - 1)
+ else return ln end
+ end
+ end
+ end
+ return nextline, io.open(p)
+ else
+ e2lib.abort("no such file: " .. p)
+ end
+end
+
+--- read the global config file
+-- local tools call this function inside collect_project_info()
+-- global tools must call this function after parsing command line options
+-- @param e2_config_file string: config file path (optional)
+-- @return bool
+-- @return error string on error
+function e2lib.read_global_config(e2_config_file)
+ e2lib.log(4, "e2lib.read_global_config()")
+ local cf = get_first_val({
+ e2lib.cmdline["e2-config"], -- command line
+ e2lib.osenv["E2_CONFIG"], -- environment
+ })
+ local cf_path
+ if cf then
+ cf_path = { cf }
+ elseif e2_config_file then
+ cf_path = { e2_config_file }
+ else
+ cf_path = {
+ -- this is ordered by priority
+ string.format("%s/.e2/e2.conf-2.2", e2lib.homedir),
+ string.format("%s/.e2/e2.conf", e2lib.homedir),
+ string.format("/etc/e2/e2.conf-2.2"),
+ string.format("/etc/e2/e2.conf"),
+ }
+ end
+ -- use ipairs to keep the list entries ordered
+ for _,path in ipairs(cf_path) do
+ local c = {}
+ c.config = function(x)
+ c.data = x
+ end
+ e2lib.log(4, string.format("reading global config file: %s", path))
+ local rc = e2util.exists(path)
+ if rc then
+ e2lib.log(3, string.format("using global config file: %s", path))
+ local rc, e = e2lib.dofile_protected(path, c, true)
+ if not rc then
+ return nil, e
+ end
+ if not c.data then
+ return false, "invalid configuration"
+ end
+ e2lib.global_config = c.data
+ e2lib.use_global_config()
+ return true, nil
+ else
+ e2lib.log(4, string.format(
+ "global config file does not exist: %s", path))
+ end
+ end
+ return false, "no config file available"
+end
+
+--- use the global parameters from the global configuration
+-- this function always succeeds or aborts
+-- @return nothing
+function e2lib.use_global_config()
+ local config = e2lib.global_config
+ if not config then
+ e2lib.abort("global config not available")
+ end
+ if config.log and config.log.enable ~= nil then
+ e2lib.enable_invocation_log = config.log.enable
+ e2lib.log(3, string.format(
+ "e2lib.enable_invocation_log=%s", tostring(config.log.enable)))
+ end
+ if config.site and config.site.buildnumber_server_url ~= nil then
+ e2lib.buildnumber_server_url = config.site.buildnumber_server_url
+ e2lib.log(3, string.format("e2lib.buildnumber_server_url=%s",
+ tostring(config.site.buildnumber_server_url)))
+ end
+ -- check if type(x) == t, and abort if not.
+ local function assert_type(x, d, t)
+ if type(x) ~= t then
+ e2lib.abort(string.format("configuration error: %s", d))
+ end
+ end
+ assert_type(config.site, "config.site", "table")
+ assert_type(config.site.e2_branch, "config.site.e2_branch", "string")
+ assert_type(config.site.e2_tag, "config.site.e2_tag", "string")
+ assert_type(config.site.e2_server, "config.site.e2_server", "string")
+ assert_type(config.site.e2_location, "config.site.e2_location", "string")
+end
+
+--- get the global configuration
+-- this function always succeeds or aborts
+-- @return the global configuration
+function e2lib.get_global_config()
+ local config = e2lib.global_config
+ if not config then
+ e2lib.abort("global config not available")
+ end
+ return config
+end
+
+function e2lib.directory(p, dotfiles, noerror)
+ local dir = e2util.directory(p, dotfiles)
+ if not dir then
+ if noerror then dir = {}
+ else e2lib.abort("directory `", p, "' does not exist")
+ end
+ end
+ table.sort(dir)
+ local i = 1
+ local function nextfile(s)
+ if i > #s then return nil
+ else
+ local j = i
+ i = i + 1
+ return s[ j ]
+ end
+ end
+ return nextfile, dir
+end
+
+
+-- Hash value computation
+--
+-- Computes some hash value from data, which is fed into it
+-- using an iterator function to be provided. The iterator
+-- function is expected to accept one parameter value.
+--
+-- e2lib.compute_hash(ITER, [VALUE...])
+
+function e2lib.compute_hash(iter, ...)
+ local n, f, s
+ local i, o, e, p = e2util.pipe("sha1sum")
+ if not i then e2lib.bomb("cannot calculate hash sum: " .. o) end
+ for x in iter(...) do
+ n, f = e2util.write(i, x)
+ if not n then e2lib.bomb(f) end
+ end
+ n, f = e2util.close(i)
+ if not n then e2lib.bomb(f) end
+ s, f = e2util.read(o, 40)
+ if not s then e2lib.bomb(f) end
+ n, f = e2util.close(o)
+ if not n then e2lib.bomb(f) end
+ n, f = e2util.wait(p)
+ if not n then e2lib.bomb(f) end
+ return s
+end
+
+-- Iterator functions
+--
+-- e2lib.impairs(TABLE)
+--
+-- iterates on a table, returning only the value for each entry, not the key
+-- To be used like: for v in e2lib.impairs(table) do ... end
+--
+-- e2lib.pairs(TABLE)
+--
+-- iterates on a table, returning both key and value, not simultaneously but
+-- one by one, suitable for hash computation. This function iterates twice
+-- as often as entries are in the table. To be used like:
+-- for kv in e2lib.pairs(table) do ... end
+
+function e2lib.impairs(table)
+ local k = nil
+ local function value(t)
+ k = next(t, k)
+ return k and t[k]
+ end
+ return value, table
+end
+
+function e2lib.pairs(table)
+ local k = nil
+ local v = nil
+ local function value(t)
+ local r = v
+ if v then
+ v = nil
+ return r
+ end
+ k = next(t, k)
+ v = k and t[k]
+ return k
+ end
+ return value, table
+end
+
+-- callcmd: call a command, connecting
+-- stdin, stdout, stderr to luafile objects
+
+function e2lib.callcmd(infile, outfile, errfile, cmd)
+ -- redirect stdin
+ io.stdin:close()
+ luafile.dup2(infile:fileno(), 0)
+ -- redirect stdout
+ io.stdout:close()
+ luafile.dup2(outfile:fileno(), 1)
+ -- redirect stderr
+ io.stderr:close()
+ luafile.dup2(errfile:fileno(), 2)
+ -- run the command
+ local rc = os.execute(cmd)
+ return (rc/256)
+end
+
+-- callcmd_redirect: call a command with
+-- stdin redirected from /dev/null
+-- stdout/stderr redirected to a luafile object
+
+function e2lib.callcmd_redirect(cmd, out)
+ local devnull, pid, rc
+ devnull = luafile.open("/dev/null", "r")
+ e2lib.log(3, "+ " .. cmd)
+ pid = e2util.fork()
+ if pid == 0 then
+ rc = e2lib.callcmd(devnull, out, out, cmd)
+ os.exit(rc)
+ else
+ rc = e2util.wait(pid)
+ luafile.close(devnull)
+ return rc
+ end
+end
+
+-- callcmd_pipe: call several commands in a pipe
+-- cmds is a table of unix commands
+-- redirect endpoints to /dev/null, unless given
+-- return nil on success, descriptive string on error
+
+function e2lib.callcmd_pipe(cmds, infile, outfile)
+ local i = infile or luafile.open("/dev/null", "r")
+ local c = #cmds
+ local rc = nil
+ local rcs = {}
+ local pids = {}
+ local ers = {}
+ for n = 1, c do
+ local o, pr, fr, er, ew
+ pr, er, ew = luafile.pipe()
+ if not pr then e2lib.abort("failed to open pipe (error)") end
+ if n < c then
+ pr, fr, o = luafile.pipe()
+ if not pr then e2lib.abort("failed to open pipe") end
+ else
+ o = outfile or ew
+ end
+ e2lib.log(3, "+ " .. cmds[n])
+ local pid = e2util.fork()
+ if pid == 0 then
+ if n < c then fr:close() end
+ er:close()
+ rc = e2lib.callcmd(i, o, ew, cmds[n])
+ os.exit(rc)
+ end
+ pids[pid] = n
+ e2util.unblock(er:fileno())
+ ers[n] = er
+ ew:close()
+ if n < c then o:close() end
+ if n > 1 or not infile then i:close() end
+ i = fr
+ end
+ while c > 0 do
+ local fds = {}
+ local ifd = {}
+ for i, f in pairs(ers) do
+ local n = f:fileno()
+ table.insert(fds, n)
+ ifd[n] = i
+ end
+ local i, r = e2util.poll(-1, fds)
+ if i <= 0 then e2lib.abort("fatal poll abort " .. tostring(i)) end
+ i = ifd[fds[i]]
+ if r then
+ local x
+ repeat
+ x = ers[i]:readline()
+ if x then
+ e2lib.log(3, x)
+ end
+ until not x
+ else
+ ers[i]:close()
+ ers[i] = nil
+ c = c - 1
+ end
+ end
+ c = #cmds
+ while c > 0 do
+ local r, p = e2util.wait(-1)
+ if not r then e2lib.abort(p) end
+ local n = pids[p]
+ if n then
+ if r ~= 0 then rc = rc or r end
+ rcs[n] = r
+ pids[p] = nil
+ c = c - 1
+ end
+ end
+ return rc and "failed to execute commands in a pipe, exit codes are: "
+ .. table.concat(rcs, ", ")
+end
+
+--- call a command with stdin redirected from /dev/null, stdout/stderr
+-- captured via a pipe
+-- the capture function is called for every chunk of output that
+-- is captured from the pipe.
+-- @return unknown
+function e2lib.callcmd_capture(cmd, capture)
+ local rc, oread, owrite, devnull, pid
+ local function autocapture(...)
+ local msg = table.concat({...})
+ e2lib.log(3, msg)
+ e2lib.last_output = msg
+ end
+ e2lib.last_output = false
+ capture = capture or autocapture
+ rc, oread, owrite = luafile.pipe()
+ owrite:setlinebuf()
+ oread:setlinebuf()
+ devnull = luafile.open("/dev/null", "r")
+ e2lib.log(4, "+ " .. cmd)
+ pid = e2util.fork()
+ if pid == 0 then
+ oread:close()
+ rc = e2lib.callcmd(devnull, owrite, owrite, cmd)
+ os.exit(rc)
+ else
+ owrite:close()
+ --e2lib.log("capturing...")
+ while not oread:eof() do
+ local x = oread:readline()
+ if x then
+ --print("read: '" .. x .. "'")
+ capture(x)
+ end
+ end
+ oread:close()
+ rc = e2util.wait(pid)
+ luafile.close(devnull)
+ --e2lib.log("capturing done...")
+ --e2lib.log("exit status was " .. rc)
+ end
+ return rc
+end
+
+--- call a command, log its output to a loglevel, catch the last line of
+-- output and return it in addition to the commands return code
+-- @param cmd string: the command
+-- @param loglevel number: loglevel (optional, defaults to 3)
+-- @return number: the return code
+-- @return string: the program output, or nil
+function e2lib.callcmd_log(cmd, loglevel)
+ local e = ""
+ if not loglevel then
+ loglevel = 3
+ end
+ local function logto(output)
+ e2lib.log(loglevel, output)
+ e = e .. output
+ end
+ local rc = e2lib.callcmd_capture(cmd, logto)
+ return rc, e
+end
+
+-- Protected execution of Lua code
+--
+-- e2lib.dofile_protected(PATH, TABLE, [ALLOWNEWDEFS])
+--
+-- Runs the code in the Lua file at PATH with a restricted global environment.
+-- TABLE contains a table with the initial global environment. If ALLOWNEWDEFS
+-- is given and true, then the code may define new global variables.
+
+function e2lib.dofile_protected(path, gtable, allownewdefs)
+ local chunk, msg = loadfile(path)
+ if not chunk then
+ return false, msg
+ end
+ local t = gtable
+ -- t._G = t
+ local function checkread(t, k)
+ local x = rawget(t, k)
+ if x then return x
+ else e2lib.abort(path, ": attempt to reference undefined global variable '",
+ k, "'")
+ end
+ end
+ local function checkwrite(t, k, v)
+ e2lib.abort(path, ": attempt to set new global variable `", k, "' to ", v)
+ end
+ if not allownewdefs then
+ setmetatable(t, { __newindex = checkwrite, __index = checkread })
+ end
+ setfenv(chunk, t)
+ local s, msg = pcall(chunk)
+ if not s then
+ e2lib.abort(msg)
+ end
+ return true, nil
+end
+
+function e2lib.dofile2(path, gtable)
+ local e = new_error("error loading config file: %s", path)
+ local chunk, msg = loadfile(path)
+ if not chunk then
+ return false, e:cat(msg)
+ end
+ setfenv(chunk, gtable)
+ local s, msg = pcall(chunk)
+ if not s then
+ return false, e:cat(msg)
+ end
+ return true, nil
+end
+
+-- e2lib.locate_project_root([PATH]) -> PATH | nil
+--
+-- Locates the root directory of current project. If PATH is not given,
+-- then the current working directory is taken as the base directory from
+-- where to start.
+--
+
+function e2lib.locate_project_root(path)
+ local rc, re
+ local e = new_error("checking for project directory failed")
+ local save_path = e2util.cwd()
+ if not save_path then
+ return nil, e:append("cannot get current working directory")
+ end
+ if path then
+ rc = e2lib.chdir(path)
+ if not rc then
+ e2lib.chdir(save_path)
+ return nil, e:cat(re)
+ end
+ else
+ path = e2util.cwd()
+ if not path then
+ e2lib.chdir(save_path)
+ return nil, e:append("cannot get current working directory")
+ end
+ end
+ while true do
+ if e2util.exists(".e2/e2version") then
+ e2lib.logf(3, "project is located in: %s", path)
+ e2lib.chdir(save_path)
+ return path
+ end
+ if path == "/" then
+ break
+ end
+ rc = e2lib.chdir("..")
+ if not rc then
+ e2lib.chdir(save_path)
+ return nil, e:cat(re)
+ end
+ path = e2util.cwd()
+ if not path then
+ e2lib.chdir(save_path)
+ return nil, e:append("cannot get current working directory")
+ end
+ end
+ e2lib.chdir(save_path)
+ return nil, new_error("not in a project directory")
+end
+
+-- parse version files:
+
+function e2lib.parse_versionfile(filename)
+ local f = luafile.open(filename, "r")
+ if not f then
+ e2lib.abort("can't open version file: " .. filename)
+ end
+ local l = f:readline()
+ if not l then
+ e2lib.abort("can't parse version file: " .. filename)
+ end
+ local v = l:match("[0-9]+")
+ if not v then
+ e2lib.abort("invalid format of project version `" .. l .. "' in " .. filename)
+ end
+ --e2lib.log(4, "project version is " .. v)
+ return v
+end
+
+function e2lib.parse_e2versionfile(filename)
+ local f = luafile.open(filename, "r")
+ if not f then
+ e2lib.abort("can't open e2version file: " .. filename)
+ end
+ local l = f:readline()
+ if not l then
+ e2lib.abort("can't parse e2version file: " .. filename)
+ end
+ local match = l:gmatch("[^%s]+")
+ local v = {}
+ v.branch = match() or e2lib.abort("invalid branch name `", l, "' in e2 version file ",
+ filename)
+ v.tag = match() or e2lib.abort("invalid tag name `", l, "' in e2 version file ",
+ filename)
+ --table.print(v)
+ e2lib.log(3, "using e2 branch " .. v.branch .. " tag " .. v.tag)
+ return v
+end
+
+--- Create a temporary file.
+-- The template string is passed to the mktemp tool, which replaces
+-- trailing X characters by some random string to create a unique name.
+-- This function always succeeds (or aborts immediately).
+-- @param template string: template name (optional)
+-- @return string: name of the file
+function e2lib.mktempfile(template)
+ if not template then
+ template = string.format("%s/e2tmp.%d.XXXXXXXX", e2lib.tmpdir,
+ e2util.getpid())
+ end
+ local cmd = string.format("mktemp %s", template)
+ local mktemp = io.popen(cmd, "r")
+ if not mktemp then
+ e2lib.bomb("can't mktemp")
+ end
+ local tmp = mktemp:read()
+ if not tmp then
+ e2lib.bomb("can't mktemp")
+ end
+ mktemp:close()
+ -- register tmp for removing with rmtempfiles() later on
+ table.insert(e2lib.tmpfiles, tmp)
+ e2lib.log(4, string.format("creating temporary file: %s", tmp))
+ return tmp
+end
+
+--- remove a temporary file and remove it from the builtin list of
+-- temporary files
+-- This function always succeeds (or aborts immediately)
+-- @param path
+function e2lib.rmtempfile(tmpfile)
+ for i,v in ipairs(e2lib.tmpfiles) do
+ if v == tmpfile then
+ table.remove(e2lib.tmpfiles, i)
+ e2lib.log(4, string.format("removing temporary file: %s", tmpfile))
+ e2lib.rm(tmpfile, "-f")
+ end
+ end
+end
+
+--- Create a temporary directory.
+-- The template string is passed to the mktemp tool, which replaces
+-- trailing X characters by some random string to create a unique name.
+-- This function always succeeds (or aborts immediately).
+-- @param template string: template name (optional)
+-- @return string: name of the directory
+function e2lib.mktempdir(template)
+ if not template then
+ template = string.format("%s/e2tmp.%d.XXXXXXXX", e2lib.tmpdir,
+ e2util.getpid())
+ end
+ local cmd = string.format("mktemp -d %s", template)
+ local mktemp = io.popen(cmd, "r")
+ if not mktemp then
+ e2lib.bomb("can't mktemp")
+ end
+ local tmpdir = mktemp:read()
+ if not tmpdir then
+ e2lib.bomb("can't mktemp")
+ end
+ mktemp:close()
+ -- register tmpdir for removing with rmtempdirs() later on
+ table.insert(e2lib.tmpdirs, tmpdir)
+ e2lib.log(4, string.format("creating temporary directory: %s", tmpdir))
+ return tmpdir
+end
+
+-- remove a temporary directory and remove it from the builtin list of
+-- temporary directories
+-- This function always succeeds (or aborts immediately)
+-- @param path
+function e2lib.rmtempdir(tmpdir)
+ for i,v in ipairs(e2lib.tmpdirs) do
+ if v == tmpdir then
+ table.remove(e2lib.tmpdirs, i)
+ e2lib.log(4, string.format("removing temporary directory: %s", tmpdir))
+ e2lib.rm(tmpdir, "-fr")
+ end
+ end
+end
+
+--- remove temporary directories registered with e2lib.mktempdir()
+-- This function does not support error checking and is intended to be
+-- called from the e2lib.finish() function.
+function e2lib.rmtempdirs()
+ e2lib.chdir("/") -- avoid being inside a temporary directory
+ while #e2lib.tmpdirs > 0 do
+ e2lib.rmtempdir(e2lib.tmpdirs[1])
+ end
+end
+
+--- remove temporary files registered with e2lib.mktempfile()
+-- This function does not support error checking and is intended to be
+-- called from the e2lib.finish() function.
+function e2lib.rmtempfiles()
+ while #e2lib.tmpfiles > 0 do
+ e2lib.rmtempfile(e2lib.tmpfiles[1])
+ end
+end
+
+--- call the rm tool with flags and filename
+-- @param file string: the file parameter
+-- @param flags string: flags to pass to rm (optional)
+-- @return bool
+-- @return an error object on failure
+function e2lib.rm(file, flags)
+ if not flags then
+ flags = ""
+ end
+ local args = string.format("%s %s", flags, file)
+ return e2lib.call_tool("rm", args)
+end
+
+--- call the touch tool with flags and filename
+-- @param file string: the file parameter
+-- @param flags string: flags to pass to touch (optional)
+-- @returns bool
+function e2lib.touch(file, flags)
+ if not flags then
+ flags = ""
+ end
+ local args = string.format("%s %s", flags, file)
+ return e2lib.call_tool("touch", args)
+end
+
+--- call the mkdir command
+-- @param dir string: the directory name
+-- @param flags string: flags to pass to mkdir
+-- @return bool
+-- @return the last line ouf captured output
+function e2lib.mkdir(dir, flags)
+ if not flags then
+ flags = ""
+ end
+ local args = string.format("%s %s", flags, dir)
+ return e2lib.call_tool("mkdir", args)
+end
+
+--- call the patch command
+-- @param dir string: the directory name
+-- @param flags string: flags to pass to mkdir
+-- @return bool
+-- @return the last line ouf captured output
+function e2lib.patch(args)
+ return e2lib.call_tool("patch", args)
+end
+
+--- call a tool
+-- @param tool string: tool name as registered in the tools library
+-- @param args string: arguments
+-- @return bool
+-- @return string: the last line ouf captured output
+function e2lib.call_tool(tool, args)
+ local cmd = transport.get_tool(tool)
+ if not tool then
+ e2lib.bomb("trying to call invalid tool: " .. tostring(tool))
+ end
+ local flags = transport.get_tool_flags(tool)
+ if not flags then
+ e2lib.bomb("invalid tool flags for tool: " .. tostring(tool))
+ end
+ local call = string.format("%s %s %s", cmd, flags, args)
+ local rc, e = e2lib.callcmd_log(call)
+ if rc ~= 0 then
+ return false, e
+ end
+ return true, e
+end
+
+--- call git
+-- @param gitdir string: GIT_DIR (optional, defaults to ".git")
+-- @param subtool string: git tool name
+-- @param args string: arguments to pass to the tool (optional)
+-- @return bool
+-- @return an error object on failure
+function e2lib.git(gitdir, subtool, args)
+ local rc, re
+ local e = new_error("calling git failed")
+ if not gitdir then
+ gitdir = ".git"
+ end
+ if not args then
+ args = ""
+ end
+ local git, re = transport.get_tool("git")
+ if not git then
+ return false, e:cat(re)
+ end
+ local call = string.format("GIT_DIR='%s' %s %s %s", gitdir, git,
+ subtool, args)
+ rc, re = e2lib.callcmd_log(call)
+ if rc ~= 0 then
+ e:append(call)
+ return false, e:cat(re)
+ end
+ return true, e
+end
+
+--- call the svn command
+-- @param args string
+-- @return bool
+function e2lib.svn(args)
+ return e2lib.call_tool("svn", args)
+end
+
+--- call the ln command
+-- @param destination string: destination name
+-- @param link string: link name
+-- @return bool
+-- @return the last line of captured output
+function e2lib.symlink(dst, link)
+ local args = string.format("-s '%s' '%s'", dst, link)
+ return e2lib.call_tool("ln", args)
+end
+
+--- call the chmod command
+-- @param mode string: the new mode
+-- @param path string: path
+-- @return bool
+-- @return the last line ouf captured output
+function e2lib.chmod(mode, path)
+ local args = string.format("'%s' '%s'", mode, path)
+ return e2lib.call_tool("chmod", args)
+end
+
+--- call the mv command
+-- @param src string: source name
+-- @param dst string: destination name
+-- @return bool
+-- @return the last line ouf captured output
+function e2lib.mv(src, dst)
+ local args = string.format("'%s' '%s'", src, dst)
+ return e2lib.call_tool("mv", args)
+end
+
+--- call the cp command
+-- @param src string: source name
+-- @param dst string: destination name
+-- @param flags string: additional flags
+-- @return bool
+-- @return the last line ouf captured output
+function e2lib.cp(src, dst, flags)
+ if not flags then
+ flags = ""
+ end
+ local args = string.format("%s '%s' '%s'", flags, src, dst)
+ return e2lib.call_tool("cp", args)
+end
+
+--- call the curl command
+-- @param args
+-- @return bool
+-- @return an error object on failure
+function e2lib.curl(args)
+ return e2lib.call_tool("curl", args)
+end
+
+--- call the ssh command
+-- @param args
+-- @return bool
+-- @return an error object on failure
+function e2lib.ssh(args)
+ return e2lib.call_tool("ssh", args)
+end
+
+--- call the scp command
+-- @param args
+-- @return bool
+-- @return an error object on failure
+function e2lib.scp(args)
+ return e2lib.call_tool("scp", args)
+end
+
+--- call the rsync command
+-- @param args
+-- @return bool
+-- @return an error object on failure
+function e2lib.rsync(args)
+ return e2lib.call_tool("rsync", args)
+end
+
+--- call the catcommand
+-- @param args
+-- @return bool
+-- @return an error object on failure
+function e2lib.cat(args)
+ return e2lib.call_tool("cat", args)
+end
+
+--- check if dir is a directory
+-- @param dir string: path
+-- @return bool
+function e2lib.isdir(dir)
+ local args = string.format("-d '%s'", dir)
+ return e2lib.call_tool("test", args)
+end
+
+--- check if path is a file
+-- @param dir string: path
+-- @return bool
+function e2lib.isfile(path)
+ local args = string.format("-f '%s'", path)
+ return e2lib.call_tool("test", args)
+end
+
+--- check if path is a file
+-- @param dir string: path
+-- @return bool
+function e2lib.sha1sum(path)
+ local args = string.format("'%s'", path)
+ local sha1sum, re = transport.get_tool("sha1sum")
+ if not sha1sum then
+ return nil, re
+ end
+ local sha1sum_flags, re = transport.get_tool_flags("sha1sum")
+ if not sha1sum_flags then
+ return nil, re
+ end
+ local cmd = string.format("%s %s %s", sha1sum, sha1sum_flags, args)
+ local p, msg = io.popen(cmd, "r")
+ if not p then
+ return nil, new_error(msg)
+ end
+ local out, msg = p:read("*l")
+ local sha1, file = out:match("(%S+) (%S+)")
+ if type(sha1) ~= "string" then
+ return nil, new_error(msg)
+ end
+ return sha1
+end
+
+--- call the e2-su command
+-- @param args string
+-- @return bool
+function e2lib.e2_su(args)
+ return e2lib.call_tool("e2-su", args)
+end
+
+--- call the e2-su-2.2 command
+-- @param args string
+-- @return bool
+function e2lib.e2_su_2_2(args)
+ return e2lib.call_tool("e2-su-2.2", args)
+end
+
+--- call the tar command
+-- @param args string
+-- @return bool
+function e2lib.tar(args)
+ return e2lib.call_tool("tar", args)
+end
+
+--- get system architecture
+function e2lib.get_sys_arch()
+ local rc, re
+ local e = new_error("getting host system architecture failed")
+ local uname = transport.get_tool("uname")
+ local cmd = string.format("%s -m", uname)
+ local p, msg = io.popen(cmd, "r")
+ if not p then
+ return nil, e:cat(msg)
+ end
+ local l, msg = p:read()
+ if not l then
+ return nil, e:cat(msg)
+ end
+ local arch = l:match("(%S+)")
+ if not arch then
+ return nil, e:append("%s: %s: cannot parse", cmd, l)
+ end
+ return arch, nil
+end
+
+--- return a table of parent directories
+-- @param path string: path
+-- @return a table of parent directories, including path.
+function e2lib.parentdirs(path)
+ local i = 2
+ local t = {}
+ local stop = false
+ while true do
+ local px
+ p = path:find("/", i)
+ if not p then
+ p = #path
+ stop = true
+ end
+ px = path:sub(1, p)
+ table.insert(t, px)
+ i = p + 1
+ if stop then
+ break
+ end
+ end
+ return t
+end
+
+--- write a string to a file
+-- @param file string: filename
+-- @param data string: data
+-- @return bool
+-- @return nil, or an error string
+function e2lib.write_file(file, data)
+ local f = io.open(file, "w")
+ if not f then
+ return false, "open failed"
+ end
+ local rc = f:write(data)
+ if not rc then
+ return false, "write failed"
+ end
+ f:close()
+ return true, nil
+end
+
+--- read a file into a string
+-- @param file string: filename
+-- @return string: the file content
+-- @return nil, or an error object
+function e2lib.read_file(file)
+ local f, msg = io.open(file, "r")
+ if not f then
+ return nil, new_error("%s", msg)
+ end
+ local s, msg = f:read("*a")
+ if not s then
+ return nil, new_error("%s", msg)
+ end
+ f:close()
+ return s, nil
+end
+
+--- read a template file, located relative to the current template directory
+-- @param file string: relative filename
+-- @return string: the file content
+-- @return an error object on failure
+function e2lib.read_template(file)
+ local e = new_error("error reading template file")
+ local filename = string.format("%s/%s", e2lib.template_path, file)
+ local template, re = e2lib.read_file(filename)
+ if not template then
+ return nil, e:cat(re)
+ end
+ return template, nil
+end
+
+--- generate an error string from an error string and a token
+-- @param e string: error string
+-- @param token string: the token leading to the error
+-- @return string: an error string suitable for printing
+function estring(e, token)
+ return string.format("%s: %s", tostring(token), tostring(e))
+end
+
+--- parse a server:location string, taking a default server into account
+-- @param arg string: the string to parse
+-- @param dafault server string: the default server name
+-- @return a table with fields server and location, nil on error
+-- @return nil, an error string on error
+function e2lib.parse_server_location(arg, default_server)
+ local sl = {}
+ sl.server, sl.location = arg:match("(%S+):(%S+)")
+ if sl.server and sl.location then
+ return sl
+ end
+ sl.location = arg:match("(%S+)")
+ if sl.location and default_server then
+ sl.server = default_server
+ return sl
+ end
+ return nil, "can't parse location"
+end
+
+--- setup a cache
+-- @param name string: cache name
+-- @param cache_url string: where to place the cache?
+-- @param servers table: server configuration table
+-- @return a cache object
+-- @return an error object on failure
+function e2lib.setup_cache(name, cache_url, servers)
+ local e = new_error("setting up cache failed")
+ local cache, re = cache.new_cache(name, cache_url)
+ if not cache then
+ return nil, e:cat(re)
+ end
+ for name,server in pairs(servers) do
+ local flags = {}
+ flags.cachable = server.cachable
+ flags.cache = server.cache
+ flags.islocal = server.islocal
+ flags.writeback = server.writeback
+ flags.push_permissions = server.push_permissions
+ local rc, re = new_cache_entry(cache, name, server.url, flags)
+ if not rc then
+ return nil, e:cat(re)
+ end
+ end
+ return cache, nil
+end
+
+--- replace format elements, according to the table
+-- @param s string: the string to work on
+-- @param t table: a table of key-value pairs
+-- @return string
+function e2lib.format_replace(s, t)
+ -- t has the format { f="foo" } to replace %f by foo inside the string
+ -- %% is automatically replaced by %
+ local start = 1
+ while true do
+ local p = s:find("%%", start)
+ if not p then
+ break
+ end
+ t["%"] = "%"
+ for x,y in pairs(t) do
+ if s:sub(p+1, p+1) == x then
+ s = s:sub(1, p-1) .. y .. s:sub(p+2, #s)
+ start = p + #y
+ break
+ end
+ end
+ start = start + 1
+ end
+ return s
+end
+
+--- take a table of values, with integer keys and return the first string
+-- value
+-- @param a table of values
+function get_first_val(t)
+ for k, v in pairs(t) do
+ if type(v) == "string" then
+ return v
+ end
+ end
+ return nil
+end
+
+--- change directory
+-- @param path
+-- @return bool
+-- @return an error object on failure
+function e2lib.chdir(path)
+ local rc, re
+ rc, re = e2util.cd(path)
+ if not rc then
+ return false, new_error("chdir %s failed: %s", path, re)
+ end
+ return true, nil
+end
+
+--------------------------------------------------------------------------------
+
+-- "seal" modules e2util and e2lib
+
+e2lib.module("e2util", e2util)
+e2lib.module("e2lib", e2lib)
+
+e2lib.debuglogfile = io.open("/dev/null", "w")
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- state-specific loader for e2util
+
+
+require("e2util_global")
+require("luafile_ll_global")
+
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- state-specific loader for e2util
+
+
+require("e2util_local")
+require("luafile_ll_global")
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- Parsing of command-line options
+
+
+e2option = e2lib.module("e2option")
+
+local options = {}
+local optionlist = {}
+local commands = {}
+local program_name = arg[ 0 ]
+
+
+-- Option declaration
+--
+-- e2option.documentation -> STRING
+--
+-- Holds a general description string of the currently executing
+-- tool.
+--
+-- e2option.flag(NAME, [DOC, [FUNCTION]])
+--
+-- Declares a "flag" option (an option without argument) with the given
+-- name (a string), documentation string (defaults to "") and a function
+-- that will be called when the option is given on the command line.
+--
+-- e2option.option(NAME, [DOC, [DEFAULT, [FUNCTION, [ARGUMENTNAME]]]])
+--
+-- Declares an option with argument. DEFAULT defaults to "true".
+-- ARGUMENTNAME will be used in the generated usage information
+-- (see "e2option.usage()").
+--
+-- e2option.alias(NAME, OPTION)
+--
+-- Declares an alias for another option.
+
+e2option.documentation = "<no documentation available>"
+e2option.aliases = {}
+
+--- register a flag option
+-- @param name string: option name
+-- @param doc string: documentation string
+-- @param func a function to call when this option is specified
+-- @param category string: category name
+-- @return nil
+function e2option.flag(name, doc, func, category)
+ options[ name ] = {type = "flag", documentation = doc or "", name = name,
+ proc=func, default = true,
+ category = category}
+ table.insert(optionlist, name)
+end
+
+--- register an option with argument
+-- @param name string: option name
+-- @param doc string: documentation string
+-- @param default string: default value
+-- @param func a function to call when this option is specified
+-- @param argname string: argument name used in documentation (optional)
+-- @return nil
+function e2option.option(name, doc, default, func, argname)
+ options[ name ] = {type = "option", documentation = doc or "", name = name,
+ proc=func, default=default or true,
+ argumentname=argname or "ARGUMENT"}
+ table.insert(optionlist, name)
+end
+
+--- XXX e2option.command(): undocumented, never called. Remove?
+function e2option.command(name, doc, func)
+ commands[ name ] = {documentation=doc, command=func, name=name}
+end
+
+--- register an alias for an option
+-- @param alias string: alias name
+-- @param option string: name of the option to register the alias for
+-- @return nil
+function e2option.alias(alias, option)
+ if e2option.aliases[ alias ] then
+ e2lib.warn("alias `", alias, "' for option `", option, "' already exists")
+ end
+ e2option.aliases[ alias ] = option
+end
+
+
+-- Option parsing
+--
+-- e2option.parse(ARGUMENTS) -> TABLE
+--
+-- Parses the arguments given in ARGUMENTS (usually obtained via "arg")
+-- and returns a table with an entry for each option. The entry is stored
+-- under the optionname with the value given by the FUNCTION or DEFAULT
+-- arguments from the associated option declaration call ("e2option.flag()"
+-- or "e2option.option()"). The result table with additionally contain
+-- and entry named "arguments" holding an array of all non-option arguments.
+--
+-- e2option.usage([CODE])
+--
+-- Prints usage information on io.stdout and either signals an error
+-- (if interactive) or exits with status code CODE (defaults to 0).
+
+--- option_table holding options keyed by option name.
+-- The special key "arguments" holds a list of non-option command line
+-- arguments
+-- @class table
+-- @name option_table
+-- @field arguments list of additional arguments
+
+--- parse options
+-- @param args table: command line arguments (usually the arg global variable)
+-- @return table: option_table
+function e2option.parse(args)
+ local function defaultoptions()
+ local category = "Verbosity Control Options"
+ e2option.option("e2-config", "specify configuration file", nil,
+ function(arg)
+ e2lib.sete2config(arg)
+ end,
+ "FILE")
+ e2option.flag("quiet", "disable all log levels",
+ function()
+ e2lib.setlog(1, false)
+ e2lib.setlog(2, false)
+ e2lib.setlog(3, false)
+ e2lib.setlog(4, false)
+ return true
+ end,
+ category)
+ e2option.flag("verbose", "enable log levels 1-2",
+ function()
+ e2lib.setlog(1, true)
+ e2lib.setlog(2, true)
+ return true
+ end,
+ category)
+ e2option.flag("debug", "enable log levels 1-3",
+ function()
+ e2lib.setlog(1, true)
+ e2lib.setlog(2, true)
+ e2lib.setlog(3, true)
+ return true
+ end,
+ category)
+ e2option.flag("tooldebug", "enable log levels 1-4",
+ function()
+ e2lib.setlog(1, true)
+ e2lib.setlog(2, true)
+ e2lib.setlog(3, true)
+ e2lib.setlog(4, true)
+ return true
+ end,
+ category)
+ e2option.flag("vall", "enable all log levels",
+ function()
+ e2lib.setlog(1, true)
+ e2lib.setlog(2, true)
+ e2lib.setlog(3, true)
+ e2lib.setlog(4, true)
+ return true
+ end,
+ category)
+ e2option.flag("v1", "enable log level 1 (minimal)",
+ function()
+ e2lib.setlog(1, true)
+ return true
+ end,
+ category)
+ e2option.flag("v2", "enable log level 2 (verbose)",
+ function()
+ e2lib.setlog(2, true)
+ return true
+ end,
+ category)
+ e2option.flag("v3", "enable log level 3 (show user debug information)",
+ function()
+ e2lib.setlog(3, true)
+ return true
+ end,
+ category)
+ e2option.flag("v4", "enable log level 4 (show tool debug information)",
+ function()
+ e2lib.setlog(4, true)
+ return true
+ end,
+ category)
+ e2option.flag("log-debug", "enable logging of debugging output",
+ function()
+ e2lib.log_debug = true
+ return true
+ end,
+ category)
+ e2option.flag("Wall", "enable all warnings")
+ e2option.flag("Wdefault", "warn when default values are applied")
+ e2option.flag("Wdeprecated", "warn if deprecated options are used")
+ e2option.flag("Wnoother",
+ "disable all warnings not mentioned above (enabled by default)")
+ e2option.flag("Wpolicy", "warn when hurting policies")
+ e2option.flag("Whint", "enable hints to the user")
+ category = "General Options"
+ e2option.flag("help", "show usage information",
+ function()
+ e2option.usage()
+ end,
+ category)
+ e2option.flag("version", "show version number",
+ function()
+ print(_version)
+ e2lib.finish(0)
+ end,
+ category)
+ e2option.flag("licence", "show licence information",
+ function()
+ print(_version)
+ print()
+ print(_licence)
+ e2lib.finish(0)
+ end,
+ category)
+ end
+
+ local function userdefaultoptions()
+ local home = e2lib.homedir
+ if not home then return end
+ local file = home .. "/.e2/e2rc"
+ if not e2util.exists(file) then
+ return
+ end
+ local e2rc = {}
+ local rc, e = e2lib.dofile_protected(file,
+ { e2rc = function(t) e2rc = t end })
+ if not rc then
+ e2lib.abort(e)
+ end
+ for _,p in pairs(e2rc) do
+ local n=p[1]
+ local v=p[2]
+ if options[n] then
+ if options[n].type == "flag" and v then
+ e2lib.abort("argument given for flag: " .. n)
+ elseif options[n].type == "option" and not v then
+ e2lib.abort("argument missing for option: " .. n)
+ end
+ local proc = options[n].proc
+ proc(v)
+ else
+ e2lib.abort("unknown option in user defaults: " .. n)
+ end
+ end
+ end
+
+ defaultoptions()
+ userdefaultoptions()
+ local vals = {}
+ local opts={ arguments=vals }
+ local i = 1
+ while i <= #args do -- we may modify args
+ local v = args[ i ]
+ local s, e, opt, val = string.find(v, "^%-%-?([^= ]+)=(.*)$")
+ if s then
+ opt = e2option.aliases[ opt ] or opt
+ if options[ opt ] then
+ local proc = options[ opt ].proc
+ if proc then val = proc(val) end
+ opts[ opt ] = val
+ else e2option.usage(1)
+ end
+ else
+ s, e, opt = string.find(v, "^%-%-?(.*)$")
+ if s then
+ opt = e2option.aliases[ opt ] or opt
+ if options[ opt ] then
+ local proc = options[ opt ].proc
+ if options[ opt ].type == "option" then
+ if i == #args then
+ e2lib.abort("argument missing for option: " .. opt)
+ end
+ if proc then
+ opts[ opt ] = proc(args[ i + 1 ])
+ else
+ opts[ opt ] = args[ i + 1 ]
+ end
+ i = i + 1
+ else
+ if proc then
+ opts[ opt ] = proc()
+ else
+ opts[ opt ] = options[ opt ].default
+ end
+ end
+ else
+ local set = string.explode(opt)
+ for k, v in pairs(set) do
+ if not options[ v ] then
+ e2option.usage(1)
+ else
+ table.insert(args, "-" .. v)
+ end
+ end
+ end
+ else
+ table.insert(vals, v)
+ end
+ end
+ i = i + 1
+ end
+ if opts["Wdefault"] or opts["Wall"] then
+ e2lib.warn_category.WDEFAULT = true
+ end
+ if opts["Wdeprecated"] or opts["Wall"] then
+ e2lib.warn_category.WDEPRECATED = true
+ end
+ if opts["Wnoother"] then
+ e2lib.warn_category.WOTHER = false
+ end
+ if opts["Wpolicy"] or opts["Wall"] then
+ e2lib.warn_category.WPOLICY = true
+ end
+ if opts["Whint"] or opts["Wall"] then
+ e2lib.warn_category.WHINT = true
+ end
+ return opts, vals
+end
+
+--- display builtin option documentation and exit
+-- @param rc number: return code, passed to e2lib.finish()
+-- @return nil
+function e2option.usage(rc)
+ print(_version)
+ print([[
+Copyright (C) 2007-2009 by Gordon Hecker and Oskar Schirmer, emlix GmbH
+Copyright (C) 2007-2008 by Felix Winkelmann, emlix GmbH
+
+This program comes with ABSOLUTELY NO WARRANTY; This is free software,
+and you are welcome to redistribute it under certain conditions.
+Type e2 --licence for more information.
+]])
+ print(e2option.documentation)
+ local category = nil
+ for _, n in ipairs(optionlist) do
+ local opt = options[n]
+ if category ~= opt.category then
+ print()
+ category = opt.category
+ if category then
+ print(category .. ":")
+ end
+ end
+ io.write(" -")
+ if #n > 1 then io.write("-") end
+ io.write(n)
+ if opt.type == "option" then
+ io.write("=", opt.argumentname)
+ elseif #n < 4 then
+ io.write("\t")
+ end
+ print("\t" .. opt.documentation)
+ end
+ print()
+ for k, v in pairs(commands) do
+ io.write(" ", k, command.documentation)
+ print()
+ end
+ e2lib.finish(rc)
+end
+
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Low-level file-system and process operations.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <poll.h>
+#include <fcntl.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+
+#ifndef LOCAL
+# define ENTRY_POINT luaopen_e2util_global
+#else
+# define ENTRY_POINT luaopen_e2util_local
+#endif
+
+
+static char buffer[ PATH_MAX + 1 ];
+
+
+/* e2util.fork() -> pid
+ | nil, ERRORMESSAGE
+
+ Forks a subprocess.
+*/
+
+static int
+lua_fork(lua_State *lua)
+{
+ int rc;
+ fflush(0);
+ rc = fork();
+
+ if(rc < 0) {
+ lua_pushnil(lua);
+ lua_pushstring(lua, (char *)strerror(errno));
+ return 2;
+ }
+
+ lua_pushnumber(lua, rc);
+ return 1;
+}
+
+
+/* e2util.cwd() -> STRING
+
+ Returns the current working directory.
+ */
+
+static int
+get_working_directory(lua_State *lua)
+{
+ char *cwd = getcwd(buffer, sizeof(buffer));
+
+ if(cwd == NULL) lua_pushnil(lua);
+ else lua_pushstring(lua, buffer);
+
+ return 1;
+}
+
+
+/* e2util.realpath(PATH) -> PATH' | nil
+
+ If PATH names an existing object in the file-system, then this
+ function will return the absolute, canonical representation of PATH,
+ otherwise nil is returned.
+*/
+
+static int
+get_realpath(lua_State *lua)
+{
+ const char *p = luaL_checkstring(lua, 1);
+
+ if(realpath(p, buffer) == NULL) lua_pushnil(lua);
+ else lua_pushstring(lua, buffer);
+
+ return 1;
+}
+
+
+/* e2util.stat(PATH, [FOLLOWLINKS?]) -> TABLE | nil
+
+ Returns stat(3) information for the file system object designated by PATH.
+ If FOLLOWLINKS? is not given or false, then the returned information will
+ apply to the actual symbolic link, if PATH designates one. Otherwise
+ the file pointed to by the link is taken. Returns a table with the
+ following entries:
+
+ dev device-id (number)
+ ino inode-number (number)
+ mode permissions and access mode (number)
+ nlink number of hard links (number)
+ uid user id (number)
+ gid group id (number)
+ rdev device id for char of block special files (number)
+ size file size (number)
+ atime access time (number)
+ mtime modification time (number)
+ ctime change time (number)
+ blksize block size (number)
+ blocks number of blocks (number)
+
+ type one of the following strings:
+
+ block-special
+ character-special
+ fifo-special
+ regular
+ directory
+ symbolic-link
+ socket
+ unknown
+ */
+
+static int
+get_file_statistics(lua_State *lua)
+{
+ const char *p = luaL_checkstring(lua, 1);
+ static struct stat statbuf;
+ int fl = lua_gettop(lua) > 1 && lua_toboolean(lua, 2);
+ int s;
+
+ if(!fl) s = lstat(p, &statbuf);
+ else s = stat(p, &statbuf);
+
+ if(s == 0) {
+ lua_newtable(lua);
+ int t = lua_gettop(lua);
+ lua_pushstring(lua, "dev");
+ lua_pushnumber(lua, statbuf.st_dev);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "ino");
+ lua_pushnumber(lua, statbuf.st_ino);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "mode");
+ lua_pushnumber(lua, statbuf.st_mode);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "nlink");
+ lua_pushnumber(lua, statbuf.st_nlink);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "uid");
+ lua_pushnumber(lua, statbuf.st_uid);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "gid");
+ lua_pushnumber(lua, statbuf.st_gid);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "rdev");
+ lua_pushnumber(lua, statbuf.st_rdev);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "size");
+ lua_pushnumber(lua, statbuf.st_size);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "atime");
+ lua_pushnumber(lua, statbuf.st_atime);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "mtime");
+ lua_pushnumber(lua, statbuf.st_mtime);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "ctime");
+ lua_pushnumber(lua, statbuf.st_ctime);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "blksize");
+ lua_pushnumber(lua, statbuf.st_blksize);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "blocks");
+ lua_pushnumber(lua, statbuf.st_blocks);
+ lua_rawset(lua, t);
+ lua_pushstring(lua, "type");
+
+ switch(statbuf.st_mode & S_IFMT) {
+ case S_IFBLK: lua_pushstring(lua, "block-special"); break;
+ case S_IFCHR: lua_pushstring(lua, "character-special"); break;
+ case S_IFIFO: lua_pushstring(lua, "fifo-special"); break;
+ case S_IFREG: lua_pushstring(lua, "regular"); break;
+ case S_IFDIR: lua_pushstring(lua, "directory"); break;
+ case S_IFLNK: lua_pushstring(lua, "symbolic-link"); break;
+ case S_IFSOCK: lua_pushstring(lua, "socket"); break;
+ default: lua_pushstring(lua, "unknown");
+ }
+
+ lua_rawset(lua, t);
+ }
+ else lua_pushnil(lua);
+
+ return 1;
+}
+
+
+/* e2util.readlink(PATH) -> PATH' | nil
+
+ Returns the path pointed to by the symbolic link PATH or nil, if the
+ link does not exist.
+*/
+
+static int
+read_symbolic_link(lua_State *lua)
+{
+ const char *p = luaL_checkstring(lua, 1);
+ int len;
+
+ len = readlink(p, buffer, sizeof(buffer));
+
+ if(len > -1) lua_pushlstring(lua, buffer, len);
+ else lua_pushnil(lua);
+
+ return 1;
+}
+
+
+/* e2util.directory(PATH, [DOTFILES?]) -> TABLE | nil
+
+ Returns an array with the contents of the directory designated by PATH.
+ If DOTFILES? is given and true then files beginning with "." are also
+ included in the directory listing.
+*/
+
+static int
+get_directory(lua_State *lua)
+{
+ const char *p = luaL_checkstring(lua, 1);
+ int df = lua_gettop(lua) > 1 && lua_toboolean(lua, 2);
+ DIR *dir = opendir(p);
+
+ if(dir == NULL) lua_pushnil(lua);
+ else {
+ struct dirent *de;
+ int i = 1;
+
+ lua_newtable(lua);
+
+ for(;;) {
+ de = readdir(dir);
+
+ if(de == NULL) break;
+
+ if(df || de->d_name[ 0 ] != '.') {
+ lua_pushstring(lua, de->d_name);
+ lua_rawseti(lua, -2, i++);
+ }
+ }
+
+ closedir(dir);
+ }
+
+ return 1;
+}
+
+
+/* e2util.tempnam(DIR) -> PATH
+
+ Returns a random temporary pathname.
+ */
+
+static int
+create_temporary_filename(lua_State *lua)
+{
+ const char *dir = luaL_checkstring(lua, 1);
+ lua_pushstring(lua, tempnam(dir, "e2"));
+ return 1;
+}
+
+
+/* e2util.exists(PATH, [EXECUTABLE?]) -> BOOL
+
+ Returns true if the file given in PATH exists. If EXECUTABLE? is given
+ and true, then it is also checked whether the file is executable.
+*/
+
+static int
+file_exists(lua_State *lua)
+{
+ int amode = R_OK;
+ const char *f = luaL_checkstring(lua, 1);
+
+ if(lua_gettop(lua) > 1 && lua_toboolean(lua, 2)) amode = X_OK;
+
+ lua_pushboolean(lua, access(f, amode) == 0);
+ return 1;
+}
+
+
+/* e2util.cd(PATH)
+
+ Changes the current working directory to PATH.
+*/
+
+static int
+change_directory(lua_State *lua)
+{
+ int rc;
+ const char *ptr = luaL_checkstring(lua, 1);
+ rc = chdir(ptr);
+ if(rc < 0) {
+ char buf[256];
+ strerror_r(errno, buf, sizeof(buf));
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, buf);
+ return 2;
+ }
+ lua_pushboolean(lua, 1);
+ lua_pushnil(lua);
+ return 2;
+}
+
+/* e2util.symlink(OLDPATH, NEWPATH)
+
+ Creates a symbolic link named NEWPATH which contains the string OLDPATH.
+*/
+
+static int
+create_symlink(lua_State *lua)
+{
+ const char *old = luaL_checkstring(lua, 1);
+ const char *new = luaL_checkstring(lua, 2);
+
+ lua_pushboolean(lua, symlink(old, new) == 0);
+ return 1;
+}
+
+
+/* e2util.pipe(COMMAND, [ARG...]) -> FDIN, FDOUT, FDERR, PID
+ | nil, ERRORMESSAGE
+
+ Invokes a subcommand and returns two file-descriptors for writing to stdin
+ and/or reading from stdout/stderr of the executing subprocess, respectively.
+ File-descriptors are named as viewn from the child process.
+*/
+
+static int
+run_pipe(lua_State *lua)
+{
+ int in[ 2 ], out[ 2 ], err[ 2 ];
+ char **argv;
+ int n;
+
+ if(pipe(in) != 0) return 0;
+ else if(pipe(out) != 0) {
+ close(in[ 0 ]);
+ close(in[ 1 ]);
+ return 0;
+ }
+ else if(pipe(err) != 0) {
+ close(out[ 0 ]);
+ close(out[ 1 ]);
+ close(in[ 0 ]);
+ close(in[ 1 ]);
+ return 0;
+ }
+ else {
+ fflush(0);
+ pid_t child = fork();
+
+ if(child < 0) {
+ close(in[ 0 ]);
+ close(in[ 1 ]);
+ close(out[ 0 ]);
+ close(out[ 1 ]);
+ close(err[ 0 ]);
+ close(err[ 1 ]);
+ goto fail;
+ }
+ else if(child == 0) {
+ close(in[ 1 ]);
+
+ if(in[ 0 ] != STDIN_FILENO) {
+ dup2(in[ 0 ], STDIN_FILENO);
+ close(in[ 0 ]);
+ }
+
+ close(out[ 0 ]);
+
+ if(out[ 1 ] != STDOUT_FILENO) {
+ dup2(out[ 1 ], STDOUT_FILENO);
+ close(out[ 1 ]);
+ }
+
+ close(err[ 0 ]);
+
+ if(err[ 1 ] != STDERR_FILENO) {
+ dup2(err[ 1 ], STDERR_FILENO);
+ close(err[ 1 ]);
+ }
+
+ n = lua_gettop(lua);
+ argv = alloca(sizeof(*argv) * (n+1));
+ argv[n] = NULL;
+ while (n > 0) {
+ argv[n-1] = (char *)luaL_checkstring(lua, n);
+ n -= 1;
+ }
+ execvp(argv[ 0 ], argv);
+ goto fail;
+ }
+ else {
+ close(in[ 0 ]);
+ close(out[ 1 ]);
+ close(err[ 1 ]);
+ lua_pushnumber(lua, in[ 1 ]);
+ lua_pushnumber(lua, out[ 0 ]);
+ lua_pushnumber(lua, err[ 0 ]);
+ lua_pushnumber(lua, child);
+ return 4;
+ }
+ }
+
+ fail:
+ lua_pushnil(lua);
+ lua_pushstring(lua, (char *)strerror(errno));
+ return 2;
+}
+
+
+/* e2util.wait(PID) -> STATUS, PID
+ | nil, ERRORMESSAGE
+
+ waits for process to terminate and returns exit code.
+*/
+
+static int
+process_wait(lua_State *lua)
+{
+ pid_t pid = luaL_checkinteger(lua, 1);
+ int rc, status;
+ rc = waitpid(pid, &status, 0);
+ if (rc < 0) {
+ lua_pushnil(lua);
+ lua_pushstring(lua, (char *)strerror(errno));
+ return 2;
+ }
+ lua_pushnumber(lua, WEXITSTATUS(status));
+ lua_pushnumber(lua, rc);
+ return 2;
+}
+
+
+/* e2util.read(FD, NUM) -> STRING
+ | nil, ERRORMESSAGE
+
+ Reads characters from a file-descriptor.
+*/
+
+static int
+read_fd(lua_State *lua)
+{
+ int fd = luaL_checkinteger(lua, 1);
+ int n = luaL_checkinteger(lua, 2);
+ char *buf = (char *)malloc(n);
+ int m;
+
+ if(buf == NULL) return 0;
+
+ m = read(fd, buf, n);
+
+ if(m < 0) {
+ lua_pushnil(lua);
+ lua_pushstring(lua, (char *)strerror(errno));
+ free(buf);
+ return 2;
+ }
+ else lua_pushlstring(lua, buf, m);
+
+ free(buf);
+ return 1;
+}
+
+
+/* e2util.write(FD, STRING, [NUM]) -> NUM'
+ | nil, ERRORMESSAGE
+
+ Writes characters to a file-descriptor.
+*/
+
+static int
+write_fd(lua_State *lua)
+{
+ int fd = luaL_checkinteger(lua, 1);
+ size_t len;
+ const char *buf = luaL_checklstring(lua, 2, &len);
+ int n = lua_gettop(lua) > 2 ? luaL_checkinteger(lua, 3) : len;
+ int m;
+
+ m = write(fd, buf, n);
+
+ if(m < 0) {
+ lua_pushnil(lua);
+ lua_pushstring(lua, (char *)strerror(errno));
+ return 2;
+ }
+
+ lua_pushnumber(lua, m);
+ return 1;
+}
+
+
+/* e2util.close(FD) -> true
+ | false, ERRORMESSAGE
+
+ Close file-descriptor, returning "false" if an error occurred or "true"
+ otherwise.
+*/
+
+static int
+close_fd(lua_State *lua)
+{
+ int fd = luaL_checkinteger(lua, 1);
+
+ if(close(fd) < 0) {
+ lua_pushnil(lua);
+ lua_pushstring(lua, (char *)strerror(errno));
+ return 2;
+ }
+
+ lua_pushboolean(lua, 1);
+ return 1;
+}
+
+/* e2util.poll(TMO_MSEC, {FD...}) -> INDEX, POLLIN, POLLOUT
+ Returns 0 on timeout, <0 on error, otherwise indicates which FD triggered.
+ When muliple FDs triggered, only one is indicated.
+ With a FD given, two boolean values indicate read/writeability.
+*/
+
+static int
+poll_fd(lua_State *lua)
+{
+ int tmo = luaL_checkinteger(lua, 1);
+ int nfds = 0, f;
+ struct pollfd *fds = NULL;
+ luaL_checktype(lua, 2, LUA_TTABLE);
+ while (1) {
+ lua_rawgeti(lua, 2, nfds+1);
+ if (lua_isnil(lua, -1)) break;
+ f = luaL_checkinteger(lua, -1);
+ lua_pop(lua, 1);
+ fds = realloc(fds, (nfds+1) * sizeof(struct pollfd));
+ fds[nfds].fd = f;
+ fds[nfds].events = POLLIN | POLLOUT;
+ fds[nfds].revents = 0;
+ nfds += 1;
+ }
+ f = poll(fds, nfds, tmo);
+ if (f > 0) {
+ while (--nfds >= 0) {
+ if (fds[nfds].revents) {
+ free(fds);
+ lua_pushnumber(lua, nfds+1);
+ lua_pushboolean(lua, fds[nfds].revents & POLLIN);
+ lua_pushboolean(lua, fds[nfds].revents & POLLOUT);
+ return 3;
+ }
+ }
+ }
+ free(fds);
+ lua_pushnumber(lua, f);
+ return 1;
+}
+
+/* e2util.unblock(FD)
+ Set file to nonblocking mode
+*/
+
+static int
+unblock_fd(lua_State *lua)
+{
+ int fd = luaL_checkinteger(lua, 1);
+ int fl = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, fl | O_NONBLOCK);
+ return 0;
+}
+
+
+/* e2util.isatty(FD) -> BOOL
+
+ Returns true, if FD refers to a terminal device.
+*/
+
+static int
+is_terminal(lua_State *lua)
+{
+ int fd = luaL_checkinteger(lua, 1);
+ lua_pushboolean(lua, isatty(fd));
+ return 1;
+}
+
+/* e2util.umask(VAL)
+
+ Set the umask to VAL
+*/
+
+static int
+set_umask(lua_State *lua)
+{
+ int u = luaL_checkinteger(lua, 1);
+ umask(u);
+ return 0;
+}
+
+/* e2util.setenv(var, val, overwrite)
+
+*/
+
+static int
+do_setenv(lua_State *lua)
+{
+ const char *var = luaL_checkstring(lua, 1);
+ const char *val = luaL_checkstring(lua, 2);
+ int overwrite = lua_toboolean(lua, 3);
+ int rc;
+ rc = setenv(var, val, overwrite != 0);
+ lua_pushboolean(lua, rc == 0);
+ return 1;
+
+}
+
+/* e2util.unsetenv(var)
+
+*/
+
+static int
+do_unsetenv(lua_State *lua)
+{
+ const char *var = luaL_checkstring(lua, 1);
+ int rc;
+ rc = unsetenv(var);
+ lua_pushboolean(lua, rc == 0);
+ return 1;
+}
+
+/* e2util.exec()
+
+ call execvp() with the full argument list */
+
+static int
+do_exec(lua_State *lua)
+{
+ const int max_args = 256;
+ const char *args[max_args+1];
+ int rc, i;
+ for(i=0; i<max_args+1; i++) {
+ args[i] = luaL_optlstring(lua, i+1, NULL, NULL);
+ if(!args[i]) {
+ break;
+ }
+ }
+ if(i > max_args) {
+ lua_pushboolean(lua, 0);
+ return 1;
+ }
+ args[i] = NULL;
+ rc = execvp(args[0], &args[0]);
+ lua_pushboolean(lua, rc == 0);
+ return 1;
+}
+
+/* e2util.getpid()
+
+ get the pid of the current process */
+
+static int
+do_getpid(lua_State *lua) {
+ pid_t pid = getpid();
+ if(pid < 0 )
+ lua_pushnil(lua);
+ else
+ lua_pushinteger(lua, pid);
+ return 1;
+}
+
+/* e2util.catch_interrupt()
+
+ Establish signal handler for SIGINT that aborts. */
+
+static void
+lstop(lua_State *L, lua_Debug *ar) {
+ lua_sethook(L, NULL, 0, 0);
+ lua_getglobal(L, "e2lib");
+ lua_pushstring(L, "interrupt_hook");
+ lua_rawget(L, -2);
+ lua_call(L, 0, 0);
+}
+
+static lua_State *globalL;
+
+
+static void
+laction (int i) {
+ signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
+ terminate process (default action) */
+ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+}
+
+
+static luaL_Reg lib[] = {
+ { "cwd", get_working_directory },
+ { "realpath", get_realpath },
+ { "stat", get_file_statistics },
+ { "readlink", read_symbolic_link },
+ { "directory", get_directory },
+ { "tempnam", create_temporary_filename },
+ { "exists", file_exists },
+ { "cd", change_directory },
+ { "symlink", create_symlink },
+ { "pipe", run_pipe },
+ { "wait", process_wait },
+ { "read", read_fd },
+ { "write", write_fd },
+ { "close", close_fd },
+ { "poll", poll_fd },
+ { "unblock", unblock_fd },
+ { "fork", lua_fork },
+ { "isatty", is_terminal },
+ { "umask", set_umask },
+ { "setenv", do_setenv },
+ { "unsetenv", do_unsetenv },
+ { "exec", do_exec },
+ { "getpid", do_getpid },
+ { NULL, NULL }
+};
+
+
+int ENTRY_POINT(lua_State *lua)
+{
+ luaL_register(lua, "e2util", lib);
+ globalL = lua;
+ signal(SIGINT, laction);
+ return 1;
+}
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+local error = {}
+
+--- append a string to an error object
+-- @param format string: format string
+-- @param ... list of strings required for the format string
+-- @return table: the error object
+function error.append(e, format, ...)
+ e.count = e.count + 1
+ table.insert(e.msg, string.format(format, ...))
+ return e
+end
+
+--- insert an error object into another one
+-- @param e table: the error object
+-- @param re table: the error object to insert
+-- @return table: the error object
+function error.cat(e, re)
+ -- auto-convert strings to error objects before inserting
+ if type(re) == "string" then
+ re = new_error(re)
+ end
+ table.insert(e.msg, re)
+ e.count = e.count + 1
+ return e
+end
+
+function error.print(e, depth)
+ if not depth then
+ depth = 1
+ else
+ depth = depth + 1
+ end
+ local prefix = string.format("Error [%d]: ", depth)
+ for _,m in ipairs(e.msg) do
+ if type(m) == "string" then
+ e2lib.log(1, string.format("%s%s", prefix, m))
+ prefix = string.format("[%d] ", depth)
+ else
+ -- it's a sub error
+ m:print(depth)
+ end
+ end
+end
+
+--- set the error counter
+-- @param e the error object
+-- @param n number: new error counter setting
+-- @return nil
+function error.setcount(e, n)
+ e.count = n
+end
+
+--- get the error counter
+-- @param e the error object
+-- @return number: the error counter
+function error.getcount(e, n)
+ return e.count
+end
+
+--- create an error object
+-- @param format string: format string
+-- @param ... list of arguments required for the format string
+-- @return table: the error object
+function new_error(format, ...)
+ local e = {}
+ e.count = 0
+ e.msg = {}
+ e.append = error.append
+ e.cat = error.cat
+ e.print = error.print
+ e.setcount = error.setcount
+ e.getcount = error.getcount
+ if format then
+ e:append(format, ...)
+ end
+ return e
+end
+
+function toerror(x)
+ if type(x) == "table" then
+ return x
+ else
+ return new_error(x)
+ end
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- Map e2-versions to Lua interpreter versions
+
+
+lua_versions = {
+ [ "1" ] = "5.1.2",
+ [ "2" ] = "5.1.3",
+ latest = "@LUA_VERSION@"
+}
+
+function lua_versions.get_version(e2_version)
+ local v = e2_version or E2_VERSION
+ local lv = lua_versions[ v ]
+ return lv or lua_versions.latest
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+luafile = {}
+
+function luafile.new()
+ local f = {}
+ f.close = luafile.close
+ f.read = luafile.read
+ f.write = luafile.write
+ f.seek = luafile.seek
+ f.flush = luafile.flush
+ f.fileno = luafile.fileno
+ f.readline = luafile.readline
+ f.setlinebuf = luafile.setlinebuf
+ f.eof = luafile.eof
+ return f
+end
+
+function luafile.open(path, mode)
+ local f = luafile.new()
+ f.file = luafile_ll.fopen(path, mode)
+ if f.file then
+ return f
+ end
+ return nil
+end
+
+function luafile.fdopen(fd, mode)
+ local f = luafile.new()
+ f.file = luafile_ll.fdopen(fd, mode)
+ if f.file then
+ return f
+ end
+ return nil
+end
+
+function luafile.close(luafile)
+ if luafile and luafile.file then
+ if luafile_ll.fclose(luafile.file) then
+ luafile.file = nil
+ return true
+ end
+ end
+ return false
+end
+
+function luafile.read(luafile)
+ if luafile and luafile.file then
+ return luafile_ll.fread(luafile.file)
+ end
+ return nil
+end
+
+function luafile.write(luafile, buffer)
+ if luafile and luafile.file and buffer then
+ return luafile_ll.fwrite(luafile.file, buffer)
+ end
+ return nil
+end
+
+function luafile.readline(luafile)
+ if luafile and luafile.file then
+ return luafile_ll.fgets(luafile.file)
+ end
+ return nil
+end
+
+function luafile.seek(luafile, offset)
+ if luafile and luafile.file and offset then
+ return luafile_ll.fseek(luafile.file, offset)
+ end
+ return nil
+end
+
+function luafile.flush(luafile)
+ if luafile and luafile.file then
+ return luafile_ll.fflush(luafile.file)
+ end
+ return nil
+end
+
+function luafile.fileno(luafile)
+ if luafile and luafile.file then
+ return luafile_ll.fileno(luafile.file)
+ end
+ return nil
+end
+
+function luafile.eof(luafile)
+ if luafile and luafile.file then
+ return luafile_ll.feof(luafile.file)
+ end
+ return nil
+end
+
+function luafile.setlinebuf(luafile)
+ if luafile and luafile.file then
+ return luafile_ll.setlinebuf(luafile.file)
+ end
+ return nil
+end
+
+function luafile.pipe()
+ local rc, r, w = luafile_ll.pipe()
+ local fr, fw
+ if not rc then
+ return false, nil, nil
+ end
+ fr = luafile.fdopen(r, "r")
+ fw = luafile.fdopen(w, "w")
+ return rc, fr, fw
+end
+
+function luafile.dup2(oldfd, newfd)
+ if oldfd and newfd then
+ return luafile_ll.dup2(oldfd, newfd)
+ end
+ return nil
+end
+
+
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Low-level file-system and process operations.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#ifndef LOCAL
+# define ENTRY_POINT luaopen_luafile_ll_global
+#else
+# define ENTRY_POINT luaopen_luafile_ll_local
+#endif
+
+static int
+lua_fopen(lua_State *lua)
+{
+ FILE *f;
+ const char *file, *mode;
+ file = luaL_checkstring(lua, 1);
+ mode = luaL_checkstring(lua, 2);
+ f = fopen(file, mode);
+ if(f == NULL) {
+ lua_pushnil(lua);
+ } else {
+ lua_pushlightuserdata(lua, (void *)f);
+ }
+ return 1;
+}
+
+static int
+lua_fclose(lua_State *lua)
+{
+ FILE *f;
+ int rc;
+ f = (FILE *)lua_topointer(lua, 1);
+ if(f) {
+ rc = fclose(f);
+ lua_pushboolean(lua, (rc == 0));
+ } else {
+ lua_pushboolean(lua, 0);
+ }
+ return 1;
+}
+
+static int
+lua_fdopen(lua_State *lua)
+{
+ FILE *f;
+ int fd;
+ const char *mode;
+ fd = luaL_checkinteger(lua, 1);
+ mode = luaL_checkstring(lua, 2);
+ f = fdopen(fd, mode);
+ if(f == NULL) {
+ lua_pushnil(lua);
+ } else {
+ lua_pushlightuserdata(lua, (void *)f);
+ }
+ return 1;
+}
+
+static int
+lua_fwrite(lua_State *lua)
+{
+ FILE *f;
+ const char *b;
+ int n = 0, rc;
+ f = (FILE *)lua_topointer(lua, 1);
+ b = luaL_checkstring(lua, 2);
+ if(!f || !b) {
+ lua_pushboolean(lua, 0);
+ return 1;
+ }
+ n = strlen(b);
+ rc = fwrite(b, 1, n, f);
+ lua_pushboolean(lua, (rc == n));
+ return 1;
+}
+
+static int
+lua_fread(lua_State *lua)
+{
+ char buf[16384];
+ int rc;
+ FILE *f;
+ f = (FILE *)lua_topointer(lua, 1);
+ rc = fread(buf, 1, sizeof(buf), f);
+ if(rc>0) {
+ lua_pushlstring(lua, buf, rc);
+ } else if (rc == 0) {
+ lua_pushstring(lua, "");
+ } else {
+ lua_pushnil(lua);
+ }
+ return 1;
+}
+
+static int
+lua_fgets(lua_State *lua)
+{
+ FILE *f;
+ char buf[16384], *rc;
+ f = (FILE *)lua_topointer(lua, 1);
+ if(!f) {
+ lua_pushnil(lua);
+ return 1;
+ }
+ rc = fgets(buf, sizeof(buf), f);
+ if(!rc) {
+ lua_pushnil(lua);
+ return 1;
+ }
+ lua_pushstring(lua, buf);
+ return 1;
+}
+
+static int
+lua_fseek(lua_State *lua)
+{
+ int rc;
+ long offset;
+ FILE *f;
+ f = (FILE *)lua_topointer(lua, 1);
+ offset = luaL_checklong(lua, 2);
+ if(!f) {
+ lua_pushboolean(lua, 0);
+ return 1;
+ }
+ rc = fseek(f, offset, SEEK_SET);
+ lua_pushboolean(lua, rc == 0);
+ return 1;
+}
+
+static int
+lua_fflush(lua_State *lua)
+{
+ int rc;
+ FILE *f;
+ f = (FILE *)lua_topointer(lua, 1);
+ if(!f) {
+ lua_pushnil(lua);
+ return 1;
+ }
+ rc = fflush(f);
+ lua_pushboolean(lua, rc == 0);
+ return 1;
+}
+
+static int
+lua_pipe(lua_State *lua)
+{
+ int fd[2];
+ int rc;
+ rc = pipe(fd);
+ lua_pushboolean(lua, rc == 0);
+ lua_pushnumber(lua, fd[0]);
+ lua_pushnumber(lua, fd[1]);
+ return 3;
+}
+
+static int
+lua_fileno(lua_State *lua)
+{
+ FILE *f;
+ int fd;
+ f = (FILE *)lua_topointer(lua, 1);
+ if(!f) {
+ lua_pushnil(lua);
+ return 1;
+ }
+ fd = fileno(f);
+ lua_pushinteger(lua, fd);
+ return 1;
+}
+
+static int
+lua_eof(lua_State *lua)
+{
+ FILE *f;
+ int eof;
+ f = (FILE *)lua_topointer(lua, 1);
+ if(!f) {
+ lua_pushnil(lua);
+ return 1;
+ }
+ eof = feof(f);
+ lua_pushboolean(lua, eof);
+ return 1;
+}
+
+static int
+lua_setlinebuf(lua_State *lua)
+{
+ FILE *f;
+ f = (FILE *)lua_topointer(lua, 1);
+ if(!f) {
+ lua_pushboolean(lua, 0);
+ return 1;
+ }
+ setlinebuf(f);
+ lua_pushboolean(lua, 1);
+ return 1;
+}
+
+static int
+lua_dup2(lua_State *lua)
+{
+ int oldfd, newfd, rc;
+ oldfd = luaL_checkinteger(lua, 1);
+ newfd = luaL_checkinteger(lua, 2);
+ rc = dup2(oldfd, newfd);
+ lua_pushboolean(lua, (rc == 0));
+ return 1;
+}
+
+static luaL_Reg lib[] = {
+ { "fopen", lua_fopen },
+ { "fdopen", lua_fdopen },
+ { "fclose", lua_fclose },
+ { "fwrite", lua_fwrite },
+ { "fread", lua_fread },
+ { "fseek", lua_fseek },
+ { "fflush", lua_fflush },
+ { "fileno", lua_fileno },
+ { "feof", lua_eof },
+ { "fgets", lua_fgets },
+ { "setlinebuf", lua_setlinebuf },
+ { "pipe", lua_pipe },
+ { "dup2", lua_dup2 },
+ { NULL, NULL }
+};
+
+int ENTRY_POINT(lua_State *lua)
+{
+ luaL_register(lua, "luafile_ll", lib);
+ return 1;
+}
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+--- functions with '1' postfix take url strings as parameter. the others
+-- take server / location
+
+generic_git = {}
+
+--- clone a git repository
+-- @param surl url to the server
+-- @param location location relative to the server url
+-- @param skip_checkout bool: pass -n to git clone?
+-- @return true on success, false on error
+-- @return nil, an error object on failure
+function generic_git.git_clone_url1(surl, location, destdir, skip_checkout)
+ if (not surl) or (not location) or (not destdir) then
+ e2lib.abort("git_clone2(): missing parameter")
+ end
+ local rc, re
+ local e = new_error("cloning git repository")
+ local full_url = string.format("%s/%s", surl, location)
+ local u, re = url.parse(full_url)
+ if not u then
+ return false, e:cat(re)
+ end
+ local src, re = generic_git.git_url1(u)
+ if not src then
+ return false, e:cat(re)
+ end
+ local flags = ""
+ if skip_checkout then
+ flags = "-n"
+ end
+ local cmd = string.format("git clone %s --quiet %s %s", flags, src, destdir)
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+--- git branch wrapper
+-- @param gitwc string: path to the git repository
+-- @param track bool: use --track or --no-track
+-- @param branch string: name of the branch to create
+-- @param start_point string: where to start the branch
+-- @return bool
+-- @return nil, an error object on failure
+function generic_git.git_branch_new1(gitwc, track, branch, start_point)
+ -- git branch [--track|--no-track] <branch> <start_point>
+ local f_track = nil
+ if track == true then
+ f_track = "--track"
+ else
+ f_track = "--no-track"
+ end
+ local cmd = string.format(
+ "cd \"%s\" && git branch %s \"%s\" \"%s\"",
+ gitwc, f_track, branch, start_point)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, new_error("creating new branch failed")
+ end
+ return true, nil
+end
+
+--- git checkout wrapper
+-- @param gitwc string: path to the git repository
+-- @param branch name of the branch to checkout
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_checkout1(gitwc, branch)
+ e2lib.log(3, string.format("checking out branch: %s", branch))
+ -- git checkout <branch>
+ local cmd = string.format(
+ "cd %s && git checkout \"%s\"",
+ gitwc, branch)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, new_error("git checkout failed")
+ end
+ return true, nil
+end
+
+--- git rev-list wrapper function
+-- @param gitdir string: GIT_DIR
+-- @param ref string: a reference, according to the git manual
+-- @return string: the commit id matching the ref parameter, or nil on error
+-- @return an error object on failure
+function generic_git.git_rev_list1(gitdir, ref)
+ e2lib.log(4, string.format("git_rev_list(): %s %s",
+ tostring(gitdir), tostring(ref)))
+ local e = new_error("git rev-list failed")
+ local rc, re
+ local tmpfile = e2lib.mktempfile()
+ local args = string.format("--max-count=1 '%s' -- >'%s'", ref, tmpfile)
+ rc, re = e2lib.git(gitdir, "rev-list", args)
+ if not rc then
+ return false, e -- do not include the low-level error here
+ end
+ local f, msg = io.open(tmpfile, "r")
+ if not f then
+ return nil, e:cat(msg)
+ end
+ local rev = f:read()
+ f:close()
+ e2lib.rmtempfile(tmpfile)
+ if (not rev) or (not rev:match("^%S+$")) then
+ return nil, new_error("can't parse git rev-list output")
+ end
+ if rev then
+ e2lib.log(4, string.format("git_rev_list: %s", rev))
+ else
+ e2lib.log(4, string.format("git_rev_list: unknown ref: %s", ref))
+ end
+ return rev, nil
+end
+
+--- initialize a git repository
+-- @param rurl string: remote url
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_init_db1(rurl)
+ if (not rurl) then
+ e2lib.abort("git_init_db1(): missing parameter")
+ end
+ local e = new_error("running git_init_db")
+ local rc, re
+ local u, re = url.parse(rurl)
+ if not u then
+ return false, e:cat(re)
+ end
+ local rc = false
+ local cmd = nil
+ local gitdir = string.format("/%s", u.path)
+ local gitcmd = string.format(
+ "mkdir -p \"%s\" && GIT_DIR=\"%s\" git init-db --shared", gitdir, gitdir)
+ if u.transport == "ssh" or u.transport == "scp" or
+ u.transport == "rsync+ssh" then
+ local ssh = transport.get_tool("ssh")
+ cmd = string.format("%s '%s' '%s'", ssh, u.server, gitcmd)
+ elseif u.transport == "file" then
+ cmd = gitcmd
+ else
+ return false, e:append("transport not supported: %s", u.transport)
+ end
+ rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, e:append("error running git init-db")
+ end
+ return true, nil
+end
+
+--- do a git push
+-- @param gitdir string: absolute path to a gitdir
+-- @param rurl string: remote url
+-- @param refspec string: a git refspec
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_push1(gitdir, rurl, refspec)
+ if (not rurl) or (not gitdir) or (not refspec) then
+ e2lib.abort("git_push1(): missing parameter")
+ end
+ local rc, re
+ local e = new_error("git push failed")
+ local u, re = url.parse(rurl)
+ if not u then
+ return false, e:cat(re)
+ end
+ local remote_git_url, re = generic_git.git_url1(u)
+ if not remote_git_url then
+ return false, e:cat(re)
+ end
+ -- GIT_DIR=gitdir git push remote_git_url refspec
+ local cmd = string.format("GIT_DIR=\"%s\" git push \"%s\" \"%s\"",
+ gitdir, remote_git_url, refspec)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- do a git remote-add
+-- @param lurl string: local git repo
+-- @param rurl string: remote url
+-- @param name string: remote name
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_remote_add1(lurl, rurl, name)
+ if (not lurl) or (not rurl) or (not name) then
+ e2lib.abort("missing parameter")
+ end
+ local rc, re
+ local e = new_error("git remote-add failed")
+ local lrepo, re = url.parse(lurl)
+ if not lrepo then
+ return false, e:cat(re)
+ end
+ local rrepo, re = url.parse(rurl)
+ if not rrepo then
+ return false, e:cat(re)
+ end
+ local giturl, re = generic_git.git_url1(rrepo)
+ if not giturl then
+ return false, e:cat(re)
+ end
+ -- git remote add <name> <giturl>
+ local cmd = string.format(
+ "cd \"/%s\" && git remote add \"%s\" \"%s\"", lrepo.path, name, giturl)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- translate a url to a git url
+-- @param u url table
+-- @return string: the git url
+-- @return an error object on failure
+function generic_git.git_url1(u)
+ e2lib.log(4, string.format("git_url(%s)", tostring(u)))
+ local giturl
+ if u.transport == "ssh" or u.transport == "scp" or
+ u.transport == "rsync+ssh" then
+ giturl = string.format("git+ssh://%s/%s", u.server, u.path)
+ elseif u.transport == "file" then
+ giturl = string.format("/%s", u.path)
+ else
+ return nil, new_error("transport not supported: %s", u.transport)
+ end
+ return giturl, nil
+end
+
+--- clone a git repository by server and location
+-- @param cache
+-- @param server
+-- @param location
+-- @param destdir string: destination directory
+-- @param skip_checkout bool: pass -n to git clone?
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_clone_from_server(c, server, location, destdir,
+ skip_checkout)
+ local rc, re
+ local e = new_error("cloning git repository")
+ local surl, re = cache.remote_url(c, server, location)
+ if not surl then
+ return false, e:cat(re)
+ end
+ local rc, re = generic_git.git_clone_url1(surl, "", destdir, skip_checkout)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- initialize a git repository
+-- @param c a cache
+-- @param server string: server name
+-- @param location string: location
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_init_db(c, server, location)
+ local rc, re
+ local e = new_error("initializing git repository")
+ local rurl, re = cache.remote_url(c, server, location)
+ if not rurl then
+ return false, e:cat(re)
+ end
+ local rc, re = generic_git.git_init_db1(rurl)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- do a git push
+-- @param c a cache
+-- @param gitdir string: gitdir
+-- @param server string: server name
+-- @param location string: location
+-- @param refspec string: a git refspec
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_push(c, gitdir, server, location, refspec)
+ local rc, re
+ local e = new_error("git push failed")
+ local rurl, re = cache.remote_url(c, server, location)
+ if not rurl then
+ return false, e:cat(re)
+ end
+ return generic_git.git_push1(gitdir, rurl, refspec)
+end
+
+--- do a git config query
+-- @param gitdir string: gitdir
+-- @param query string: query to pass to git config
+-- @return string: the value printed to stdout by git config, or nil
+-- @return an error object on failure
+function generic_git.git_config(gitdir, query)
+ local rc, re
+ local e = new_error("running git config")
+ local tmpfile = e2lib.mktempfile()
+ local cmd = string.format("GIT_DIR=\"%s\" git config \"%s\" > %s",
+ gitdir, query, tmpfile)
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ e:append("git config failed")
+ return nil, e
+ end
+ local git_output = e2lib.read_line(tmpfile)
+ if not git_output then
+ return nil, e:append("can't read git output from temporary file")
+ end
+ e2lib.rmtempfile(tmpfile)
+ return git_output, nil
+end
+
+--- do a git add
+-- @param gitdir string: gitdir (optional, default: .git)
+-- @param args string: args to pass to git add
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_add(gitdir, args)
+ local rc, re
+ local e = new_error("running git add")
+ if not gitdir then
+ gitdir = ".git"
+ end
+ local cmd = string.format("GIT_DIR=\"%s\" git add '%s'",
+ gitdir, args)
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ return nil, e:cat(re)
+ end
+ return true, nil
+end
+
+--- do a git commit
+-- @param gitdir string: gitdir (optional, default: .git)
+-- @param args string: args to pass to git add
+-- @return bool
+-- @return an error object on failure
+function generic_git.git_commit(gitdir, args)
+ local rc, re
+ local e = new_error("git commit failed")
+ return e2lib.git("commit", gitdir, args)
+end
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+--- create a hash context
+-- @return a hash context object, or nil on error
+-- @return nil, an error string on error
+local function hash_start()
+ local hc = {}
+ for k,v in pairs(hash) do
+ hc[k] = v
+ end
+ hc.data = ""
+ return hc
+end
+
+--- add hash data
+-- @param hc the hash context
+-- @param data string: data
+local function hash_append(hc, data)
+ -- append the data
+ hc.data = hc.data .. data
+end
+
+local function hash_line(hc, data)
+ hash_append(hc, data .. "\n")
+end
+
+--- add hash data
+-- @param hc the hash context
+-- @return the hash value, or nil on error
+-- @return an error string on error
+local function hash_finish(hc)
+ -- write hash data to a temporary file
+ hc.tmpdir = e2lib.mktempdir()
+ hc.tmpfile = string.format("%s/hashdata", hc.tmpdir)
+ hc.f = io.open(hc.tmpfile, "w")
+ hc.f:write(hc.data)
+ hc.f:close()
+
+ -- hash data and read the hash value
+ local cmd = string.format("sha1sum %s", hc.tmpfile)
+ hc.p = io.popen(cmd, "r")
+ local s = hc.p:read()
+ if not s then
+ return nil, "calculating hash value failed"
+ end
+ hc.p:close()
+
+ -- parse the output from sha1sum
+ hc.sha1 = s:match("(%S+)")
+ if not hc.sha1 then
+ return nil, "calculating hash value failed"
+ end
+ e2lib.rmtempdir(hc.tmpdir)
+ return hc.sha1
+end
+
+hash = {}
+hash.hash_start = hash_start
+hash.hash = hash_append
+hash.hash_line = hash_line
+hash.hash_finish = hash_finish
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- checks uses of undeclared global variables
+-- All global variables must be 'declared' through a regular assignment
+-- (even assigning nil will do) in a main chunk before being used
+-- anywhere or assigned to inside a function.
+--
+
+local mt = getmetatable(_G)
+if mt == nil then
+ mt = {}
+ setmetatable(_G, mt)
+end
+
+mt.__declared = {}
+
+local function what ()
+ local d = debug.getinfo(3, "S")
+ return d and d.what or "C"
+end
+
+mt.__newindex = function (t, n, v)
+ if not mt.__declared[n] then
+ local w = what()
+ if w ~= "main" and w ~= "C" then
+ error("assign to undeclared variable '"..n.."'", 2)
+ end
+ mt.__declared[n] = true
+ end
+ rawset(t, n, v)
+end
+
+mt.__index = function (t, n)
+ if not mt.__declared[n] and what() ~= "C" then
+ error("variable '"..n.."' is not declared", 2)
+ end
+ return rawget(t, n)
+end
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+local tools = {
+ which = { name = "which", flags = "", optional = false },
+ curl = { name = "curl", flags = "", optional = false },
+ ssh = { name = "ssh", flags = "", optional = false },
+ scp = { name = "scp", flags = "", optional = false },
+ rsync = { name = "rsync", flags = "", optional = false },
+ git = { name = "git", flags = "", optional = false },
+ cvs = { name = "cvs", flags = "", optional = true },
+ svn = { name = "svn", flags = "", optional = true },
+ mktemp = { name = "mktemp", flags = "", optional = false },
+ rm = { name = "rm", flags = "", optional = false },
+ mkdir = { name = "mkdir", flags = "", optional = false },
+ cp = { name = "cp", flags = "", optional = false },
+ ln = { name = "ln", flags = "", optional = false },
+ mv = { name = "mv", flags = "", optional = false },
+ tar = { name = "tar", flags = "", optional = false },
+ sha1sum = { name = "sha1sum", flags = "", optional = false },
+ md5sum = { name = "md5sum", flags = "", optional = false },
+ chmod = { name = "chmod", flags = "", optional = false },
+ test = { name = "test", flags = "", optional = false },
+ cat = { name = "cat", flags = "", optional = false },
+ touch = { name = "touch", flags = "", optional = false },
+ uname = { name = "uname", flags = "", optional = false },
+ patch = { name = "patch", flags = "", optional = false },
+ ["e2-su"] = { name = E2_PREFIX .. "/bin/e2-su", flags = "",
+ optional = false },
+ ["e2-su-2.2"] = { name = E2_PREFIX .. "/bin/e2-su-2.2", flags = "",
+ optional = false },
+}
+
+
+--- get a tool command
+-- @param name string: the tool name
+-- @return string: the tool command, nil on error
+local function get_tool(name)
+ if not tools[name] then
+ e2lib.bomb("looking up invalid tool: " .. tostring(name))
+ end
+ return tools[name].path
+end
+
+--- get tool flags
+-- @param name string: the tool name
+-- @return string: the tool flags
+local function get_tool_flags(name)
+ if not tools[name] then
+ e2lib.bomb("looking up flags for invalid tool: " ..
+ tostring(name))
+ end
+ return tools[name].flags or ""
+end
+
+--- set a tool command and flags
+-- @param name string: the tool name
+-- @param value string: the new tool command
+-- @param flags string: the new tool flags. Optional.
+-- @return bool
+-- @return nil, an error string on error
+local function set_tool(name, value, flags)
+ if not tools[name] then
+ return false, "invalid tool setting"
+ end
+ if type(value) == "string" then
+ tools[name].name = value
+ end
+ if type(flags) == "string" then
+ tools[name].flags = flags
+ end
+ e2lib.log(3, string.format("setting tool: %s=%s flags=%s",
+ name, tools[name].name, tools[name].flags))
+ return true, nil
+end
+
+--- fetch a file from a server
+-- @param surl url to the server
+-- @param location location relative to the server url
+-- @param destdir where to store the file locally
+-- @param destname filename of the fetched file
+-- @return true on success, false on error
+-- @return an error object on failure
+function fetch_file(surl, location, destdir, destname)
+ e2lib.log(4, string.format("%s: %s %s %s", "fetch_file()", surl,
+ location, destdir))
+ if not destname then
+ destname = e2lib.basename(location)
+ end
+ local rc, re
+ local e = new_error("transport: fetching file failed")
+ local u, re = url.parse(surl)
+ if not u then
+ return false, e:cat(re)
+ end
+ -- create the destination directory
+ rc, re = e2lib.mkdir(destdir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local template = string.format("%s/%s.XXXXXXXXXX", destdir, destname)
+ local tmpfile_path = e2lib.mktempfile(template)
+ local tmpfile = e2lib.basename(tmpfile_path)
+ -- fetch the file to the temporary directory
+ if u.transport == "file" or
+ u.transport == "http" or
+ u.transport == "https" then
+ -- use special flags here
+ local curlflags = "--create-dirs --silent --show-error --fail"
+ local args = string.format("%s %s/%s -o %s/%s",
+ curlflags, u.url, location, destdir, tmpfile)
+ rc, re = e2lib.curl(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ elseif u.transport == "rsync+ssh" then
+ local user
+ if u.user then
+ user = string.format("%s@", u.user)
+ else
+ user = ""
+ end
+ -- rsync --rsh="ssh" "server:sourcefile" "destdir/destfile"
+ local rsh = string.format("%s %s", tools.ssh.name,
+ tools.ssh.flags)
+ local args = string.format(
+ "--rsh=\"%s\" '%s%s:/%s/%s' '%s/%s'",
+ rsh, user, u.servername, u.path, location,
+ destdir, tmpfile)
+ rc, re = e2lib.rsync(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ elseif u.transport == "scp" or
+ u.transport == "ssh" then
+ local user
+ if u.user then
+ user = string.format("%s@", u.user)
+ else
+ user = ""
+ end
+ local args = string.format(
+ "'%s%s:/%s/%s' '%s/%s'",
+ user, u.servername, u.path, location,
+ destdir, tmpfile)
+ rc, re = e2lib.scp(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ else
+ e:append("unknown transport method: %s", u.transport)
+ return false, e
+ end
+ -- move the file into place atomically
+ local src = string.format("%s/%s", destdir, tmpfile)
+ local dst = string.format("%s/%s", destdir, destname)
+ rc, re = e2lib.mv(src, dst)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- file was moved away above, but remove it from the list anyway
+ e2lib.rmtempfile(tmpfile_path)
+ return true, nil
+end
+
+--- push a file to a server
+-- @param sourcefile local file
+-- @param durl url to the destination server
+-- @param location location relative to the server url
+-- @param push_permissions string: permissions to use on the destination
+-- side. Works with rsync+ssh only.
+-- @return true on success, false on error
+-- @return nil, an error string on error
+function push_file(sourcefile, durl, location, push_permissions)
+ e2lib.log(4, string.format("%s: %s %s %s %s", "transport.push_file()",
+ sourcefile, durl, location, tostring(push_permissions)))
+ local rc, e
+ e = new_error("error pushing file to server")
+ durl = string.format("%s/%s", durl, location)
+ local u, re = url.parse(durl)
+ if not u then
+ return e:cat(re)
+ end
+ if u.transport == "file" then
+ local destdir = string.format("/%s", e2lib.dirname(u.path))
+ local destname = e2lib.basename(u.path)
+ -- split directories, to apply permissions to all newly
+ -- created parent directories, too.
+ local dirs = e2lib.parentdirs(destdir)
+ local mkdir_perm = ""
+ if push_permissions then
+ mkdir_perm = string.format("--mode \"%s\"",
+ push_permissions)
+ end
+ for _,d in ipairs(dirs) do
+ local mkdir_flags = string.format("-p %s", mkdir_perm)
+ rc, re = e2lib.mkdir(d, mkdir_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ local dst = string.format("%s/%s", destdir, destname)
+ rc, re = e2lib.cp(sourcefile, dst)
+ if not rc then
+ return false, re
+ end
+ if push_permissions then
+ rc, re = e2lib.chmod(push_permissions, dst)
+ if not rc then
+ return false, re
+ end
+ end
+ elseif u.transport == "rsync+ssh" then
+ local destdir = string.format("/%s", e2lib.dirname(u.path))
+ local destname = e2lib.basename(u.path)
+ local user
+ if u.user then
+ user = string.format("%s@", u.user)
+ else
+ user = ""
+ end
+ local mkdir_perm = ""
+ local rsync_perm = ""
+ if push_permissions then
+ mkdir_perm = string.format("--mode \"%s\"",
+ push_permissions)
+ rsync_perm = string.format("--perms --chmod \"%s\"",
+ push_permissions)
+ end
+ -- split directories, to apply permissions to all newly
+ -- created parent directories, too.
+ local dirs = e2lib.parentdirs(destdir)
+ local tmp = e2lib.mktempfile()
+ local f = io.open(tmp, "w")
+ for _,d in ipairs(dirs) do
+ local s = string.format("mkdir -p %s \"%s\"\n",
+ mkdir_perm, d)
+ e2lib.log(4, s)
+ f:write(s)
+ end
+ f:close()
+ -- run the mkdir script via ssh
+ local args = string.format("'%s%s' <'%s'", user, u.servername,
+ tmp)
+ rc, re = e2lib.ssh(args)
+ if not rc then
+ return false, re
+ end
+ e2lib.rmtempfile(tmp)
+ local rsh = string.format("%s %s", tools.ssh.name,
+ tools.ssh.flags)
+ -- rsync --rsh="ssh" "sourcefile" "destfile"
+ local args = string.format(
+ "%s --rsh='%s' '%s' '%s%s:/%s/%s'",
+ rsync_perm, rsh, sourcefile,
+ user, u.servername, destdir, destname)
+ rc, re = e2lib.rsync(args)
+ if not rc then
+ return false, re
+ end
+ elseif u.transport == "scp" or
+ u.transport == "ssh" then
+ if push_permissions then
+ e:append("ssh/scp transport does not support "..
+ "permission settings")
+ return false, e
+ end
+ local destdir = string.format("/%s", e2lib.dirname(u.path))
+ local destname = e2lib.basename(u.path)
+ local user
+ if u.user then
+ user = string.format("%s@", u.user)
+ else
+ user = ""
+ end
+ local args = string.format("'%s%s' mkdir -p '%s'",
+ user, u.servername, destdir)
+ rc, re = e2lib.ssh(args)
+ if not rc then
+ return false, re
+ end
+ local args = string.format("'%s' '%s%s:%s/%s'",
+ sourcefile, user, u.servername, destdir, destname)
+ rc, re = e2lib.scp(args)
+ if not rc then
+ return false, re
+ end
+ else
+ e:append("push_file is not implemented for this transport: %s",
+ u.transport)
+ return false, e
+ end
+ return true, nil
+end
+
+--- fetch a file from a server
+-- @param surl url to the server
+-- @param location location relative to the server url
+-- @return true on success, false on error
+-- @return nil, an error string on error
+function file_path(surl, location)
+ e2lib.log(4, string.format("%s: %s %s", "file_path()", surl,
+ location))
+ local e = new_error("can't get path to file")
+ local u, re = url.parse(surl)
+ if not u then
+ return nil, e:cat(re)
+ end
+ if u.transport ~= "file" then
+ return nil, e:append("transport does not support file_path()")
+ end
+ local path = string.format("/%s/%s", u.path, location)
+ return path
+end
+
+--- check if a tool is available
+-- @param name string a valid tool name
+-- @return bool
+-- @return nil, an error string on error
+function check_tool(name)
+ local tool = tools[name]
+ if not tool.path then
+ local which = string.format("which \"%s\"", tool.name)
+ local p = io.popen(which, "r")
+ tool.path = p:read()
+ p:close()
+ if not tool.path then
+ e2lib.log(3, string.format(
+ "tool not available: %s", tool.name))
+ return false, "tool not available"
+ end
+ end
+ e2lib.log(4, string.format(
+ "tool available: %s (%s)",
+ tool.name, tool.path))
+ return true
+end
+
+--- initialize the library
+-- @return bool
+function init()
+ local error = false
+ for tool,t in pairs(tools) do
+ local rc = check_tool(tool)
+ if not rc then
+ local warn = "Warning"
+ if not t.optional then
+ error = true
+ warn = "Error"
+ end
+ e2lib.log(1, string.format(
+ "%s: tool is not available: %s",
+ warn, tool))
+ end
+ end
+ if error then
+ return false, "missing mandatory tools"
+ end
+ return true, nil
+end
+
+transport = {}
+transport.check_tool = check_tool
+transport.get_tool = get_tool
+transport.get_tool_flags = get_tool_flags
+transport.set_tool = set_tool
+transport.init = init
+transport.fetch_file = fetch_file
+transport.push_file = push_file
+transport.file_path = file_path
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+--- parse
+-- @param url the url to parse
+-- @return a table holding all parsed parts of the url, or nil on error
+-- @return nil, or an error string on error
+function parse(url)
+ local u = {}
+ --- url
+ -- @class table
+ -- @name url
+ -- @field url the original url as passed to the parse() function
+ -- @field transport the transport type
+ -- @field server the server part
+ -- @field path the path relative to the server
+ -- @field servername the server name from the server part
+ -- @field user the user name from the server part (optional)
+ -- @field pass the password from the server part (optional)
+ if not url then
+ return nil, "missing parameter: url"
+ end
+ u.url = url
+ -- parse: transport://server/path
+ u.transport, u.server, u.path =
+ u.url:match("(%S+)://([^/]*)(%S*)")
+ if not u.transport then
+ return nil, "can't parse url"
+ end
+ -- remove leading slashes from the path
+ u.path = u.path:match("^[/]*(%S*)")
+ -- parse the server part
+ if u.server:match("(%S+):(%S+)@(%S+)") then
+ -- user:pass@host
+ u.user, u.pass, u.servername =
+ u.server:match("(%S+):(%S+)@(%S+)")
+ elseif u.server:match("(%S+)@(%S+)") then
+ -- user@host
+ u.user, u.servername = u.server:match("(%S+)@(%S+)")
+ else
+ u.servername = u.server
+ end
+ return u, nil
+end
+
+url = {}
+url.parse = parse
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+SCRIPTS =
+GLOBALLUATOOLS = e2-create-project e2-fetch-project e2-install-e2 e2-root
+GLOBALSHTOOLS = e2-locate-project-root e2ssh
+GLOBALTOOLS = $(GLOBALLUATOOLS) $(GLOBALSHTOOLS)
+CLEAN_FILES = *~ $(GLOBALTOOLS) *.lc e2 $(SCRIPTS) *.lua e2-su *.sh e2.conf
+
+
+.PHONY: all install uninstall clean
+
+all: e2 e2-root e2global.lc $(SCRIPTS) e2-su $(GLOBALLUATOOLS:=.lc) \
+ $(GLOBALSHTOOLS:=.sh) e2.conf
+
+install: all
+ for i in $(GLOBALLUATOOLS) ; do \
+ install -m 755 $$i.lc $(DESTDIR)$(TOOLDIR)/$$i ; \
+ ln -sf e2 $(DESTDIR)$(BINDIR)/$$i ; \
+ done
+ for i in $(GLOBALSHTOOLS) ; do \
+ install -m 755 $$i.sh $(DESTDIR)$(BINDIR)/$$i ; \
+ done
+ install -m 755 e2-root $(DESTDIR)$(TOOLDIR)/
+ install -m 4754 -o root -g $(E2_GROUP) e2-su $(DESTDIR)$(BINDIR)/
+ install -m 755 e2 $(DESTDIR)$(BINDIR)/
+ install -m 644 e2global.lc $(DESTDIR)$(LIBDIR)/
+ install -d $(SYSCONFDIR)
+ if [ ! -f "$(SYSCONFDIR)/e2.conf" ] ; then \
+ install -m 644 e2.conf $(SYSCONFDIR)/e2.conf ; \
+ fi
+ install -m 644 e2.conf $(SYSCONFDIR)/e2.conf.sample
+ install -d -m 2775 -g $(E2_GROUP) $(LOCALSTATEDIR)
+
+uninstall:
+ for i in $(GLOBALLUATOOLS) ; do \
+ rm -f $(DESTDIR)$(TOOLDIR)/$$i ; \
+ rm -f $(DESTDIR)$(BINDIR)/$$i ; \
+ done
+ for i in $(GLOBALSHTOOLS) ; do \
+ rm -f $(DESTDIR)$(BINDIR)/$$i ; \
+ done
+ rm -f $(DESTDIR)$(TOOLDIR)/e2-root
+ rm -f $(DESTDIR)$(BINDIR)/e2-su
+ rm -f $(DESTDIR)$(BINDIR)/e2
+ rm -f $(DESTDIR)$(LIBDIR)/e2global.lc
+
+doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+clean:
+ rm -f $(CLEAN_FILES)
+
+e2global.lc: config.lua e2global.lua
+ $(BUILD_LUAC) -o $@ $^
+
+%.lua: %.lua.in
+ $(TOPLEVEL)/scripts/genscript.sh $< $@
+
+%.lc: %.lua
+ $(BUILD_LUAC) -o $@ $<
+
+e2-su: e2-su.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+%.sh: %.sh.in
+ $(TOPLEVEL)/scripts/genscript.sh $< $@
+
+e2: e2.lc
+ echo "#!$(LIBEXECDIR)/e2-lua-$(LUA_VERSION)" >$@
+ cat $< >>$@
+
+e2-root: e2-root.lc
+ echo "#!$(LIBEXECDIR)/e2-lua-$(LUA_VERSION)" >$@
+ cat $< >>$@
+
+e2.conf: e2.conf.in
+ $(TOPLEVEL)/scripts/genscript.sh $< $@
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- Program configuration
+
+E2_VERSION = "@E2_VERSION@"
+E2_COMMIT = "@E2_COMMIT@"
+E2_PREFIX = "@E2_PREFIX@"
+E2_SYNTAX = "@E2_SYNTAX@"
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+package.path="@LIBDIR@/?.lc;@LIBDIR@/?.lua"
+package.cpath="@LIBDIR@/?.so"
+
+require("e2global")
+e2lib.init()
+
+local doc = [[
+usage: e2-create-project [<option> ...] [<server>:]<location>
+
+Create a new project and store it on <server> in <location>.
+<server> defaults to '%s'.
+]]
+e2option.documentation = string.format(doc, e2lib.default_projects_server)
+
+local opts = e2option.parse(arg)
+local rc, e = e2lib.read_global_config()
+if not rc then
+ e2lib.abort(e)
+end
+e2lib.init2()
+local e = new_error("creating project failed")
+
+local config, re = e2lib.get_global_config()
+if not config then
+ e2lib.abort(e:cat(re))
+end
+local cachedir = e2lib.mktempdir()
+local scache, re = e2lib.setup_cache("temporary cache", cachedir,
+ config.servers)
+if not scache then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.log_invocation(nil, arg)
+
+-- standard global tool setup finished
+
+--e2hook.run_hook(nil, "tool-start", nil, "e2-create-project")
+
+if e2lib.osenv["E2_LOCAL_TAG"] and e2lib.osenv["E2_LOCAL_BRANCH"] then
+ e2lib.local_e2_branch = e2lib.osenv["E2_LOCAL_BRANCH"]
+ e2lib.local_e2_tag = e2lib.osenv["E2_LOCAL_TAG"]
+elseif e2lib.osenv["E2_LOCAL_TAG"] then
+ e2lib.local_e2_branch = "-"
+ e2lib.local_e2_tag = e2lib.osenv["E2_LOCAL_TAG"]
+elseif e2lib.osenv["E2_LOCAL_BRANCH"] then
+ e2lib.local_e2_branch = e2lib.osenv["E2_LOCAL_BRANCH"]
+ e2lib.local_e2_tag = "^"
+else
+ e2lib.local_e2_branch = config.site.e2_branch
+ e2lib.local_e2_tag = config.site.e2_tag
+end
+
+if #opts.arguments ~= 1 then
+ e2option.usage(1)
+end
+
+local sl, re = e2lib.parse_server_location(opts.arguments[1],
+ e2lib.default_projects_server)
+if not sl then
+ e2lib.abort(e:cat(re))
+end
+
+local p = {}
+p.version = 2 -- the project version
+p.e2version = string.format("%s %s", e2lib.local_e2_branch,
+ e2lib.local_e2_tag)
+p.server = sl.server -- the server
+p.location = sl.location -- the project location
+p.name = e2lib.basename(sl.location) -- the project basename
+p.server = sl.server -- the server
+
+-- create the server side structure
+local tmpdir = e2lib.mktempdir()
+e2lib.chdir(tmpdir)
+
+local version = string.format("%d\n", p.version)
+local empty = ""
+files = {
+ { filename = "version", content=version },
+ { filename = "proj/.keep", content=empty },
+ { filename = "git/.keep", content=empty },
+ { filename = "files/.keep", content=empty },
+ { filename = "cvs/.keep", content=empty },
+ { filename = "svn/.keep", content=empty },
+}
+for _,f in ipairs(files) do
+ local dir = e2lib.dirname(f.filename)
+ rc, re = e2lib.mkdir(dir, "-p")
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+ rc, re = e2lib.write_file(f.filename, f.content)
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+ local sourcefile = string.format("%s/%s", tmpdir, f.filename)
+ local flocation = string.format("%s/%s", p.location, f.filename)
+ local cache_flags = {}
+ rc, re = cache.push_file(scache, sourcefile, p.server, flocation,
+ cache_flags)
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+end
+e2lib.chdir("/")
+e2lib.rmtempdir(tmpdir)
+
+local tmpdir = e2lib.mktempdir()
+e2lib.chdir(tmpdir)
+
+-- create the initial repository on server side
+local rlocation = string.format("%s/proj/%s.git", p.location, p.name)
+local rc, re = generic_git.git_init_db(scache, p.server, rlocation)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+-- works up to this point
+
+-- create the initial (git) repository
+local url = string.format("file://%s/.git", tmpdir)
+rc, re = e2lib.git(nil, "init-db")
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+local gitignore = e2lib.read_template("gitignore")
+if not gitignore then
+ e2lib.abort(re)
+end
+local chroot, re = e2lib.read_template("proj/chroot")
+if not chroot then
+ e2lib.abort(re)
+end
+local licences, re = e2lib.read_template("proj/licences")
+if not licences then
+ e2lib.abort(re)
+end
+local env, re = e2lib.read_template("proj/env")
+if not env then
+ e2lib.abort(re)
+end
+-- XXX moge pconfig to builtin template table file
+local pconfig = [[
+-- e2factory project configuration file --
+
+e2project {
+ name = "%s",
+ release_id = "%s",
+ default_results = { },
+ chroot_arch = "x86_32",
+}
+]]
+pconfig = string.format(pconfig, p.name, p.name)
+local name = string.format("%s\n", p.name)
+local release_id = string.format("%s\n", p.name) -- use the name for now
+local version = string.format("%s\n", p.version)
+local e2version = string.format("%s\n", p.e2version)
+local syntax = string.format("%s\n", e2lib.default_e2.local_syntax)
+local empty = ""
+files = {
+ { filename = ".e2/.keep", content=empty },
+ { filename = "in/.keep", content=empty },
+ { filename = "log/.keep", content=empty },
+ { filename = "proj/init/.keep", content=empty },
+ { filename = "res/.keep", content=empty },
+ { filename = "src/.keep", content=empty },
+ { filename = "proj/chroot", content=chroot },
+ { filename = "proj/licences", content=licences },
+ { filename = "proj/env", content=env },
+ { filename = "proj/config", content=pconfig },
+ { filename = ".e2/e2version", content=e2version },
+ { filename = ".e2/version", content=version },
+ { filename = ".e2/syntax", content=syntax },
+ { filename = ".gitignore", content=gitignore },
+}
+for _,f in ipairs(files) do
+ local dir = e2lib.dirname(f.filename)
+ rc, re = e2lib.mkdir(dir, "-p")
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+ rc, re = e2lib.write_file(f.filename, f.content)
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+ rc, re = e2lib.git(nil, "add", f.filename)
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+end
+rc, re = e2lib.git(nil, "commit", "-m \"project setup\"")
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+local refspec = "master:refs/heads/master"
+local rlocation = string.format("%s/proj/%s.git", p.location, p.name)
+rc, re = generic_git.git_push(scache, ".git", p.server, rlocation, refspec)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.chdir("/")
+e2lib.rmtempdir(tmpdir)
+e2lib.finish()
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+e2_install_e2 = "@LIBEXECDIR@/e2-lua-@LUA_VERSION@ @TOOLDIR@/e2-install-e2"
+
+package.path="@LIBDIR@/?.lc;@LIBDIR@/?.lua"
+package.cpath="@LIBDIR@/?.so"
+
+require("e2global")
+e2lib.init()
+local e = new_error("fetching project failed")
+local doc = [[
+usage: e2-fetch-project [<option> ...] [<server>:]<location> [<destination>]
+
+fetch the project located in server:location to a directory given in
+<destination>.
+<server> defaults to '%s'.
+]]
+e2option.documentation = string.format(doc, e2lib.default_projects_server)
+
+e2option.flag("binary", "install binary local tools [broken]")
+e2option.option("branch", "retrieve a specific project branch")
+e2option.option("tag", "retrieve a specific project tag")
+
+local opts = e2option.parse(arg)
+local rc, re = e2lib.read_global_config()
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+e2lib.init2()
+
+-- get the global configuration
+local config = e2lib.get_global_config()
+
+-- setup cache
+local cachedir = e2lib.mktempdir()
+local scache, re = e2lib.setup_cache("temporary cache", cachedir,
+ config.servers)
+if not scache then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.log_invocation(nil, arg)
+
+-- standard global tool setup finished
+
+--e2hook.run_hook(nil, "tool-start", nil, "e2-fetch-project")
+
+if #opts.arguments < 1 then
+ e2lib.abort("specify path to a project to fetch")
+end
+if #opts.arguments > 2 then
+ e2lib.abort("too many arguments")
+end
+
+local sl, re = e2lib.parse_server_location(opts.arguments[1],
+ e2lib.default_projects_server)
+if not sl then
+ e2lib.abort(e:cat(re))
+end
+
+local p = {}
+p.server = sl.server
+p.location = sl.location
+p.name = e2lib.basename(p.location)
+if opts.arguments[2] then
+ p.destdir = opts.arguments[2]
+else
+ p.destdir = p.name
+end
+if opts["branch"] then
+ p.branch = opts["branch"]
+else
+ p.branch = nil
+end
+if opts["tag"] then
+ p.tag = opts["tag"]
+else
+ p.tag = nil
+end
+
+-- fetch project descriptor file
+local tmpdir = e2lib.mktempdir()
+local location = string.format("%s/version", p.location)
+local cache_flags = {}
+local rc, re = cache.fetch_file(scache, p.server, location, tmpdir, nil,
+ cache_flags)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+-- read the version from the first line
+local version_file = string.format("%s/version", tmpdir)
+local line, re = e2lib.read_line(version_file)
+if not line then
+ e2lib.abort(e:cat(re))
+end
+e2lib.rmtempdir()
+
+v = tonumber(line:match("[0-9]+"))
+if not v or v < 1 or v > 2 then
+ e2lib.abort(e:append("unhandled project version"))
+end
+
+-- version is 1 or 2
+
+-- clone the git repository
+local location = string.format("%s/proj/%s.git", p.location, p.name)
+local skip_checkout = false
+local destdir = p.destdir
+local rc, re = generic_git.git_clone_from_server(scache, p.server, location,
+ p.destdir, skip_checkout)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.chdir(p.destdir)
+
+-- checkout the desired branch, if a branch was given
+if p.branch then
+ local e = e:append("checking out branch failed: %s", p.branch)
+ local args = string.format("-n1 refs/heads/%s", p.branch)
+ local rc, re = e2lib.git(nil, "rev-list", args)
+ if rc then
+ e2lib.warnf("WOTHER", "Branch exists: %s No need to check out.", p.branch)
+ else
+ local args = string.format(
+ "--track -b '%s' 'origin/%s'", p.branch, p.branch)
+ local rc, re = e2lib.git(nil, "checkout", args)
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+ end
+end
+
+-- checkout the desired tag, if a tag was given
+if p.tag then
+ local e = e:append("checking out tag failed: %s", p.tag)
+ if p.branch then
+ -- branch and tag were specified. The working branch was created above.
+ -- Warn and go on checking out the tag...
+ e2lib.warnf("WOTHER",
+ "switching to tag '%s' after checking out branch '%s'",
+ p.tag, p.branch)
+ end
+ local args = string.format("'refs/tags/%s'", p.tag)
+ local rc, re = e2lib.git(nil, "checkout", args)
+ if not rc then
+ e2lib.abort(e:cat(re))
+ end
+end
+
+-- write project location file
+local file = ".e2/project-location"
+local data = string.format("%s\n", p.location)
+local rc, e = e2lib.write_file(file, data)
+if not rc then
+ e2lib.abort(e)
+end
+
+-- call e2-install-e2
+rc, re = e2lib.callcmd_log(e2_install_e2)
+if not rc then
+ e:append("installing local e2 failed")
+ e2lib.abort(e:cat(re))
+end
+e2lib.finish()
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+package.path = "@LIBDIR@/?.lc;@LIBDIR@/?.lua"
+package.cpath = "@LIBDIR@/?.so"
+
+local install_prefix = "@E2_PREFIX@"
+
+require("e2global")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-install-e2 [OPTION ...]
+
+Installs local tools in project environment.
+]]
+
+e2option.flag("binary", "install binary tools")
+
+local opts = e2option.parse(arg)
+local rc, e = e2lib.read_global_config()
+if not rc then
+ e2lib.abort(e)
+end
+e2lib.init2()
+local e = new_error("e2-install-e2 failed")
+
+local config = e2lib.get_global_config()
+local servers = config.servers
+if not servers then
+ e2lib.abort("no servers configured in global config")
+end
+
+local cachedir = e2lib.mktempdir()
+local scache, re = e2lib.setup_cache("temporary cache", cachedir,
+ config.servers)
+if not scache then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.log_invocation(nil, arg)
+
+-- standard global tool setup finished
+
+--e2hook.run_hook(nil, "tool-start", nil, "e2-install-e2")
+
+if #opts.arguments > 0 then
+ e2option.usage(1)
+end
+
+local root = e2lib.locate_project_root()
+if not root then
+ e2lib.abort("can't locate project root.")
+end
+
+local rc, re
+
+-- change to the project root directory
+rc, re = e2lib.chdir(root)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+-- read the version from the first line
+local line, re = e2lib.read_line(".e2/version")
+if not line then
+ e2lib.abort(e:cat(re))
+end
+
+v = tonumber(line:match("[0-9]+"))
+if not v or v < 1 or v > 2 then
+ e2lib.abort(e:append("unhandled project version"))
+end
+
+-- version is 1 or 2
+
+-- remove the old e2 source, if it exists
+rc, re = e2lib.rm(".e2/e2 .e2/bin .e2/lib", "-fr")
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+-- get e2 version
+local s = e2lib.read_line(".e2/e2version")
+local branch, tag = s:match("(%S+) (%S+)")
+if not branch or not tag then
+ e2lib.abort(e:append("cannot parse e2 version"))
+end
+local ref
+if tag == "^" then
+ e2lib.warnf("WOTHER", "using e2 version by branch")
+ ref = string.format("refs/heads/%s", branch)
+else
+ ref = string.format("refs/tags/%s", tag)
+end
+
+e2lib.logf(2, "installing tool version: %s", ref)
+
+rc, re = e2lib.chdir(".e2")
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+-- clone e2
+local server = config.site.e2_server
+local location = config.site.e2_location
+local destdir = "e2"
+local skip_checkout = false
+rc, re = generic_git.git_clone_from_server(scache, server, location, destdir,
+ skip_checkout)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.chdir(destdir)
+
+-- checkout ref
+local args = string.format("--track -b tmp '%s'", ref)
+rc, re = e2lib.git(nil, "checkout", args)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+-- build and install
+local cmd = string.format("make PREFIX='%s' local install-local",
+ install_prefix)
+rc, re = e2lib.callcmd_capture(cmd)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.finish()
--- /dev/null
+#!/bin/sh
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+export LC_ALL=C
+while [ '!' -f .e2/e2version ] ; do
+ if [ "$PWD" = "/" ] ; then
+ echo >&2 \
+ "e2-locate-project-root: Not in a project environment."
+ exit 1
+ fi
+ cd ..
+done
+echo $PWD
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+package.path = "@LIBDIR@/?.lc;@LIBDIR@/?.lua"
+package.cpath = "@LIBDIR@/?.so"
+
+env_tool = "@ENV_TOOL@"
+chroot_tool = "@CHROOT_TOOL@"
+tar_tool = "@TAR_TOOL@"
+chown_tool = "@CHOWN_TOOL@"
+rm_tool = "@RM_TOOL@"
+
+compat_2_1 = true -- be compatible with e2-2.1?
+
+function assert_chroot_environment(path)
+ fname = string.format("%s/emlix-chroot", path)
+ local f = io.open(fname, "r")
+ if not f then
+ print("not a chroot environment")
+ os.exit(99)
+ end
+ f:close()
+ return true
+end
+
+-- e2-root <command> ...
+if arg[1] == "chroot_2_2" then
+ -- 2_2_chroot <path> <command>
+ local path = arg[2]
+ local command = arg[3]
+ if (not path) or (not command) or #arg ~= 3 then
+ print("wrong arguments")
+ os.exit(99)
+ end
+ assert_chroot_environment(path)
+ local cmd = string.format("%s -i %s '%s' %s", env_tool, chroot_tool, path,
+ command)
+ local rc = os.execute(cmd)
+ os.exit(rc/256)
+elseif arg[1] == "extract_tar_2_2" then
+ -- extract_tar_2_2 <path> <tartype> <file>
+ local path = arg[2]
+ local tartype = arg[3]
+ local file = arg[4]
+ if (not path) or (not tartype) or (not file) or #arg ~= 4 then
+ print("wrong arguments")
+ os.exit(99)
+ end
+ assert_chroot_environment(path)
+ if tartype == "tar.gz" then
+ tarflags = "-z"
+ elseif tartype == "tar.bz2" then
+ tarflags = "-j"
+ elseif tartype == "tar" then
+ tarflags = ""
+ end
+ local cmd = string.format("%s -i %s %s -C '%s' -xf '%s'",
+ env_tool, tar_tool, tarflags, path, file)
+ local rc = os.execute(cmd)
+ os.exit(rc/256)
+elseif arg[1] == "set_permissions_2_2" then
+ -- set_permissions_2_2 <path>
+ local path = arg[2]
+ if (not path) then
+ print("wrong arguments")
+ os.exit(99)
+ end
+ assert_chroot_environment(path)
+ local cmd = string.format("%s -i %s root:root '%s'", env_tool, chown_tool,
+ path)
+ local rc = os.execute(cmd)
+ os.exit(rc/256)
+elseif arg[1] == "remove_chroot_2_2" then
+ -- remove_chroot_2_2 <path>
+ local path = arg[2]
+ if (not path) then
+ print("wrong arguments")
+ os.exit(99)
+ end
+ assert_chroot_environment(path)
+ local cmd = string.format("%s -i %s -fr '%s'", env_tool, rm_tool, path)
+ local rc = os.execute(cmd)
+ os.exit(rc/256)
+end
+
+-- e2-root <command> <tmpdir> <project> <result> <...>
+
+if not compat_2_1 then
+ print("unknown command")
+ os.exit(3)
+end
+
+if #arg < 4 then
+ print("too few arguments")
+ os.exit(3)
+end
+
+local call = {}
+call.username = os.getenv("USER")
+if not call.username then
+ print("can't get USER environment variable")
+ os.exit(3)
+end
+call.cmd = arg[1]
+call.tmpdir = arg[2] .. "/e2build"
+call.proj = arg[3]
+call.result = arg[4]
+call.chroot_path = call.tmpdir .. "/" .. call.username .. "/" ..
+ call.proj .. "/" .. call.result .. "/chroot"
+
+local rc = 1
+if call.cmd == "setup-chroot" then
+ rc = os.execute("mkdir -p " .. call.chroot_path ..
+ " && touch \"" .. call.chroot_path .. "/emlix-chroot\"")
+ os.exit(rc/256)
+
+elseif call.cmd == "extract-tar" then
+ call.file = arg[5]
+ local opts = "-xzf"
+ if string.match(call.file, ".bz2$") then
+ opts = "-xjf"
+ elseif string.match(call.file, ".tar$") then
+ opts = "-xf"
+ end
+ rc = os.execute("tar -C \"" .. call.chroot_path ..
+ "\" " .. opts .. " \"" .. call.file .. "\"")
+ os.exit(rc/256)
+
+elseif call.cmd == "prepare" then
+ rc = os.execute("PATH=/usr/sbin:/sbin:$PATH " ..
+ "T=\"/tmp/e2\" " ..
+ "r=\"" .. call.result .. "\" " ..
+ "chroot " .. call.chroot_path .. " " ..
+ "/bin/bash -e -x \"/tmp/e2/script/e2-runbuild-unpack-source\"")
+ os.exit(rc/256)
+
+elseif call.cmd == "runbuild" then
+ rc = os.execute("PATH=/usr/sbin:/sbin:$PATH " ..
+ "T=\"/tmp/e2\" " ..
+ "r=\"" .. call.result .. "\" " ..
+ "chroot " .. call.chroot_path .. " " ..
+ "/bin/bash -e -x \"/tmp/e2/script/e2-runbuild-build\"")
+ os.exit(rc/256)
+
+elseif call.cmd == "enter" then
+ local cmd = arg[5]
+ if cmd then
+ command = string.format(" -c \"%s\"", cmd)
+ else
+ command = ""
+ end
+ rc = os.execute("PATH=/usr/sbin:/sbin:$PATH " ..
+ "T=\"/tmp/e2\" " ..
+ "r=\"" .. call.result .. "\" " ..
+ "chroot " .. call.chroot_path .. " " ..
+ "/bin/bash --rcfile \"/tmp/e2/script/e2-runbuildrc\"" ..
+ command)
+ os.exit(rc/256)
+
+elseif call.cmd == "rm-chroot" then
+ rc = os.execute("rm -fr \"" .. call.chroot_path .. "\"")
+ os.exit(rc/256)
+
+end
+
+print("unknown command")
+os.exit(3)
+
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <limits.h>
+#include <grp.h>
+
+#ifndef TOOLDIR
+#error TOOLDIR not defined
+#endif
+#ifndef E2_ROOT_TOOL_NAME
+#define E2_ROOT_TOOL_NAME "e2-root"
+#endif
+#ifndef E2_ROOT_TOOL_PATH
+#define E2_ROOT_TOOL_PATH TOOLDIR "/" E2_ROOT_TOOL_NAME
+#endif
+
+char *tool_name = E2_ROOT_TOOL_NAME;
+char *tool_path = E2_ROOT_TOOL_PATH;
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ int i;
+ char *arg [ 1024 ];
+
+ if(argc < 2) {
+ fprintf(stderr, "this tool is not intended to be executed directly\n");
+ exit(1);
+ }
+
+ int k = 0;
+ arg[k++] = tool_name;
+ for(i=1; argv[i]; i++) {
+ if(argv[i][0] == '-')
+ continue;
+ arg[k++] = argv[i];
+ //fprintf(stderr, "%s\n");
+ }
+ arg[k] = NULL;
+
+ if(getenv("DEBUG")) {
+ printf("%s\n", E2_ROOT_TOOL_PATH);
+ for(i=0; arg[i]; i++) {
+ printf("\"%s\"\n", arg[i]);
+ }
+ }
+
+ rc = setuid(0);
+ if(rc != 0) {
+ perror("can't setuid(0)");
+ exit(1);
+ }
+
+ rc = setgid(0);
+ if(rc != 0) {
+ perror("can't setgid(0)");
+ exit(1);
+ }
+
+ rc = setgroups(0, NULL);
+ if(rc != 0) {
+ perror("can't setgroups()");
+ exit(1);
+ }
+
+ rc = execvp(E2_ROOT_TOOL_PATH, arg);
+ perror("can't exec");
+ exit(3);
+}
--- /dev/null
+--[[
+e2factory, the emlix embedded build system
+
+/etc/e2/e2.conf sample configuration file
+]]
+config {
+ site = {
+ e2_server = "projects",
+ e2_location = "e2factory.git",
+ e2_branch = "e2-2.3",
+ e2_tag = "e2-2.3.0pre1",
+ },
+ tools = {
+ cvs = { name="cvs", flags="-z9" },
+ ssh = { name="ssh", flags="-o BatchMode=true" },
+ scp = { name="scp", flags="-o BatchMode=true" },
+ },
+ cache = {
+ -- replacements:
+ -- %u: username
+ -- %l: project-location
+ -- to keep the projects independent use
+ --path = "/var/tmp/e2cache/%u/%l",
+ -- to maintain one cache for all projects use
+ path = "/var/tmp/e2cache-%u",
+ },
+ servers = {
+ upstream = {
+ url = "file://@LOCALSTATEDIR@/upstream",
+ cachable = true,
+ cache = true,
+ push_permissions = "ug+rwX,o-rwX",
+ },
+ projects = {
+ url = "file://@LOCALSTATEDIR@/projects",
+ cachable = false,
+ -- push_permissions works only with rsync+ssh.
+ push_permissions = "ug+rwX,o-rwX",
+ },
+ chroot = {
+ url = "file://@LOCALSTATEDIR@/chroot",
+ cachable = true,
+ cache = true,
+ },
+ results = {
+ url = "file://@LOCALSTATEDIR@/results",
+ cachable = true,
+ cache = true,
+ writeback = true,
+ -- push_permissions works only with rsync+ssh.
+ push_permissions = "u=rwX,go-rwX",
+ },
+ releases = {
+ url="file://@LOCALSTATEDIR@/releases",
+ cacheable = true,
+ cache = true,
+ writeback = true,
+ -- push_permissions works only with rsync+ssh.
+ push_permissions = "ug+rwX,o-rwX",
+ },
+ },
+}
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+package.path = "@LIBDIR@/?.lc;@LIBDIR@/?.lua"
+package.cpath = "@LIBDIR@/?.so"
+
+require("e2global")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2 OPTION ...
+ e2 COMMAND ARGUMENT ...
+
+"e2" is a wrapper script for invoking e2 commands. Any e2 commands called will
+be redirected either to the project-local or the global installation, depend‐
+ing on whether the invocation took place inside or outside a project tree.
+]]
+
+e2option.flag("prefix", "print installation prefix",
+ function()
+ print("@E2_PREFIX@")
+ os.exit(0)
+ end)
+
+local root = e2lib.locate_project_root()
+
+e2call = {}
+e2call.basename = e2lib.basename(arg[0])
+
+local function quoteargs(argstr) -- probably has to do escaping?
+ if #argstr == 0 then return ""
+ else return "'" .. argstr .. "'" end
+end
+
+if e2call.basename == "e2" and arg[1] and string.sub(arg[1], 1, 1) ~= "-" then
+ e2call.toolname = "e2-" .. arg[1]
+ e2call.arg_string = quoteargs(table.concat(arg, "' '", 2))
+elseif e2call.basename == "e2" then
+ e2call.toolname = "e2"
+ local opts = e2option.parse(arg)
+ if #opts == 0 then
+ e2option.usage()
+ end
+ e2lib.finish(0)
+else
+ e2call.toolname = e2call.basename
+ e2call.arg_string = quoteargs(table.concat(arg, "' '", 1))
+end
+
+-- provide install paths in the environment
+setinstallpaths()
+
+e2call.globaltool = getinstallpath("TOOLDIR") .. "/" .. e2call.toolname
+if root then
+ e2call.localtool = root .. "/.e2/bin/" .. e2call.toolname
+end
+
+if e2util.stat(e2call.globaltool) then
+ e2call.tool = e2call.globaltool
+ cmd = "@LIBEXECDIR@/e2-lua-@LUA_VERSION@ " .. e2call.tool .. " " .. e2call.arg_string
+elseif not root then
+ e2lib.abort(e2call.toolname ..
+ " is not a global tool and we're not in a project environment")
+elseif root and e2util.stat(e2call.localtool) then
+ e2call.tool = e2call.localtool
+ env = "LUA_PATH='" .. root .. "/.e2/lib/e2/?.lc;" ..
+ root .. "/.e2/lib/e2/?.lua' " ..
+ "LUA_CPATH=" .. root .. "/.e2/lib/e2/?.so"
+ cmd = env .. " " ..
+ root .. "/.e2/bin/e2-lua " ..
+ e2call.tool .. " " .. e2call.arg_string
+else
+ e2lib.abort(e2call.toolname .. " is neither local nor global tool")
+end
+
+function table_log(loglevel, t)
+ e2lib.log(loglevel, tostring(t))
+ for k,v in pairs(t) do
+ e2lib.log(loglevel, k .. "\t->\t" .. v)
+ end
+end
+
+table_log(3, e2call)
+
+e2lib.log(3, "calling " .. e2call.tool)
+e2lib.log(4, cmd)
+local rc = os.execute(cmd)
+e2lib.finish(rc/256)
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+package.path="@LIBDIR@/?.lc;@LIBDIR@/?.lua;" .. package.path
+package.cpath="@LIBDIR@/?.so;" .. package.cpath
+
+require("e2generic_global")
+
+-- project
+-- .name
+-- .path
+-- .checkout_path
+-- .server_versionfile
+-- .versionfile
+-- .version
+-- .e2versionfile
+-- .e2version
+
+
+local install = {}
+install [ "PREFIX" ] = "@E2_PREFIX@"
+install [ "BINDIR" ] = "@BINDIR@"
+install [ "LIBDIR" ] = "@LIBDIR@"
+install [ "LIBEXECDIR" ] = "@LIBEXECDIR@"
+install [ "TOOLDIR" ] = "@TOOLDIR@"
+
+
+-- getinstallpath(var)
+--
+-- lookup an installation path from the install table.
+
+function getinstallpath(var)
+ if install [ var ] == nil then
+ e2lib.abort("querying non-existent variable in getinstallpath()")
+ end
+ return install[var]
+end
+
+-- setinstallpaths()
+--
+-- set all installation paths in the environment. That enables the local
+-- tools to call the correct e2-su global tools.
+
+function setinstallpaths()
+ for var,val in pairs(install) do
+ e2util.setenv(var, val, true)
+ end
+end
+
+-- e2generic table: holds functions that must work across all future
+-- e2 versions
+
+e2generic = {}
+
+--- get the project version from the server
+-- @param a project description table
+-- @return string: the project version, or nil
+-- @return an error object on failure
+function e2generic.getprojectversionfromserver(project)
+ local e = new_error("getting project version from server failed")
+ local tmpdir = e2lib.mktempdir()
+ local location = string.format("%s/%s", project.location,
+ project.server_versionfile)
+ local rc, re = transport.fetch_file(project.surl, location, tmpdir, nil)
+ if not rc then
+ return nil, e:cat(re)
+ end
+ local vfile = string.format("%s/%s", tmpdir, project.server_versionfile)
+ local v = e2lib.parse_versionfile(vfile)
+ if not v then
+ return nil, e:cat("can't parse version file")
+ end
+ e2lib.rmtempdir(tmpdir)
+ return v, nil
+end
+
+function e2generic.getprojectversionfromproject(project)
+ return e2lib.parse_versionfile("./" .. project.versionfile)
+end
+
+function e2generic.gete2versionfromproject(project)
+ return e2lib.parse_e2versionfile(project.checkout_path .. "/" .. project.e2versionfile)
+end
+
+e2lib.module("e2generic", e2generic)
+
+-- V table: holds functions implementing the global tool interfaces for each version
+
+V = {}
--- /dev/null
+#!/bin/bash
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+exec ssh ${E2_SSH_OPTIONS} "$@"
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+### Makefile for "local" e2 subdirectory
+
+
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+CLEAN_FILES = *~ *.so linux32 config.lua *.lc *.o
+CFLAGS += -I. -I$(DESTDIR)$(INCDIR) -I$(DESTDIR)$(INCDIR)/lua-$(LUA_VERSION) \
+ -I../lua/lua-$(LUA_VERSION)/src -fPIC
+LDFLAGS += -L. -L../lua/lua-$(LUA_VERSION)/src -Wl,-R$(LIBDIR)
+LOCALLUATOOLS = \
+ build dlist dsort fetch-sources new-source ls-project \
+ playground build-numbers cf
+LOCALSHTOOLS =
+LOCALTOOLS = $(LOCALSHTOOLS) $(LOCALLUATOOLS)
+
+SYMLINKS_2_1 = lookup-server use-source prepare-cargo sync-results cleanup \
+ git-to-tar required-files buildid
+
+.PHONY: all install uninstall install install-local doc install-doc
+
+
+all: linux32 e2local.lc $(LOCALLUATOOLS:=.lc) libe2api.so
+
+install: libe2api.so
+ # install symlinks for e2-2.1 compatibility
+ for i in $(SYMLINKS_2_1); do \
+ ln -sf e2 $(DESTDIR)$(BINDIR)/e2-$$i ; \
+ done
+ for i in $(LOCALTOOLS); do \
+ ln -sf e2 $(DESTDIR)$(BINDIR)/e2-$$i ; \
+ done
+ install -m755 libe2api.so $(DESTDIR)$(LIBDIR)/
+ install -m644 e2api.h $(DESTDIR)$(INCDIR)/
+ install -m644 e2api.lua $(DESTDIR)$(LIBDIR)/
+
+install-local: all
+ test -n "$(PROJECTDIR)"
+ mkdir -p $(LOCALBINDIR) $(LOCALMAKDIR) $(LOCALLIBDIR)
+ install -m644 e2local.lc $(LOCALLIBDIR)/
+ for i in $(LOCALLUATOOLS); do \
+ install -m 755 $$i.lc $(LOCALBINDIR)/e2-$$i; \
+ done
+ for i in $(LOCALSHTOOLS); do \
+ install -m 755 $$i $(LOCALBINDIR)/e2-$$i; \
+ done
+ install -m755 linux32 $(LOCALBINDIR)/e2-linux32; \
+ install -m644 linux32.c $(LOCALMAKDIR)/
+ ln -sf $(LIBEXECDIR)/e2-lua-$(LUA_VERSION) $(LOCALBINDIR)/e2-lua
+ $(MAKE) -C make install-local
+
+doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+uninstall:
+ for i in $(SYMLINKS_2_1); do \
+ rm -f $(DESTDIR)$(BINDIR)/e2-$$i ; \
+ done
+ for i in $(LOCALTOOLS); do \
+ rm -f $(DESTDIR)$(BINDIR)/e2-$$i ; \
+ done
+ rm -f $(DESTDIR)$(LIBDIR)/libe2api.so
+ rm -f $(DESTDIR)$(INCDIR)/e2api.h
+ rm -f $(DESTDIR)$(LIBDIR)/e2api.lua
+
+clean:
+ rm -f $(CLEAN_FILES)
+
+%: %.in
+ $(TOPLEVEL)/scripts/genscript.sh $< $@
+
+%.lc: %.lua
+ $(LUAC) -o $@ $<
+
+e2local.lc: config.lua $(TOPLEVEL)/generic/strict.lua \
+ result.lua \
+ loader.lua \
+ $(TOPLEVEL)/generic/scm.git.lua \
+ $(TOPLEVEL)/generic/transport.lua \
+ $(TOPLEVEL)/generic/cache.lua \
+ $(TOPLEVEL)/generic/error.lua \
+ $(TOPLEVEL)/generic/url.lua \
+ $(TOPLEVEL)/generic/collection.lua \
+ $(TOPLEVEL)/generic/e2lib.lua \
+ $(TOPLEVEL)/generic/e2hook.lua \
+ e2tool.lua e2scm.lua git.lua svn.lua cvs.lua files.lua \
+ $(TOPLEVEL)/generic/e2option.lua \
+ $(TOPLEVEL)/generic/convenience.lua \
+ $(TOPLEVEL)/generic/sha1.lua \
+ e2build.lua $(TOPLEVEL)/generic/luafile.lua \
+ policy.lua
+ $(LUAC) -o $@ $^
+
+linux32: linux32.c
+
+libe2api.so: e2api.o
+ $(CC) -shared -o $@ $^ -I../lua/lua-$(LUA_VERSION)/src -I. $(CFLAGS)
+
+e2api.o: e2api.h
+
+testapi: testapi.o libe2api.so
+ $(CC) -o $@ $< -le2api $(LDFLAGS) -llua -lm -ldl -export-dynamic
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2-buildnumbers -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage:
+e2-buildnumbers [--no-sync]
+]]
+
+policy.register_commandline_options()
+e2option.flag("no-sync", "do not synchronize with the server")
+
+local opts = e2option.parse(arg)
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+local rc, re = e2tool.check_project_info(info)
+if not rc then
+ e2lib.abort(re)
+end
+
+-- get build mode from the command line
+local build_mode = policy.handle_commandline_options(opts, true)
+if not build_mode then
+ e2lib.abort("no build mode given")
+end
+-- apply the standard build mode to all results
+for _,res in pairs(info.results) do
+ res.build_mode = build_mode
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-buildnumbers")
+
+-- read build numbers,
+-- merge to results,
+-- flush buildids,
+-- calculate buildids,
+-- merge back,
+-- request new build numbers,
+-- write new build numbers
+
+local rc, re
+rc, re = e2tool.buildnumber_read(info)
+if not rc then
+ e2lib.abort(re)
+end
+rc, re = e2tool.buildnumber_mergetoresults(info)
+if not rc then
+ e2lib.abort(re)
+end
+-- recalculate build ids ids
+e2tool.flush_buildids(info)
+e2tool.calc_buildids(info)
+rc, re = e2tool.buildnumber_mergefromresults(info)
+if not rc then
+ e2lib.abort(re)
+end
+if opts["no-sync"] then
+ rc, re = e2tool.buildnumber_request_local(info)
+else
+ rc, re = e2tool.buildnumber_request(info)
+end
+if not rc then
+ e2lib.abort(re)
+end
+rc, re = e2tool.buildnumber_write(info)
+if not rc then
+ e2lib.abort(re)
+end
+e2tool.buildnumber_display(info.build_numbers, 1)
+e2lib.finish()
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2-build -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-build [<option> | <result> ...]
+
+build results from repository or local sources.
+]]
+
+e2option.flag("all", "build all results (default unless for working copy)")
+policy.register_commandline_options()
+e2option.flag("branch-mode", "build selected results in branch mode")
+e2option.flag("wc-mode", "build selected results in working-copy mode")
+e2option.flag("force-rebuild", "force rebuilding even if a result exists [broken]")
+e2option.flag("playground", "prepare environment but do not build")
+e2option.flag("keep", "do not remove chroot environment after build")
+e2option.flag("buildnumber", "use real build numbers")
+e2option.flag("buildid", "display buildids and exit")
+
+local opts = e2option.parse(arg)
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+local rc, re = e2tool.check_project_info(info)
+if not rc then
+ e2lib.abort(re)
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-build")
+
+-- get build mode from the command line
+local build_mode = policy.handle_commandline_options(opts, true)
+if not build_mode then
+ e2lib.abort("no build mode given")
+end
+-- apply the standard build mode to all results
+for _,res in pairs(info.results) do
+ res.build_mode = build_mode
+end
+
+-- handle result selection
+local results = {}
+if opts["all"] then
+ for r,_ in pairs(info.results) do
+ table.insert(results, r)
+ end
+elseif #opts.arguments > 0 then
+ for i,r in ipairs(opts.arguments) do
+ table.insert(results, r)
+ end
+end
+
+-- handle command line flags
+local build_mode = nil
+if opts["branch-mode"] and opts["wc-mode"] then
+ e = new_error("--branch-mode and --wc-mode are mutually exclusive")
+ e2lib.abort(e)
+end
+if opts["branch-mode"] then
+ -- selected results get a special build mode
+ build_mode = policy.default_build_mode["branch"]
+end
+if opts["wc-mode"] then
+ build_mode = policy.default_build_mode["working-copy"]
+end
+local playground = opts["playground"]
+if playground then
+ if opts.release then
+ e2lib.abort("--release and --playground are mutually exclusive")
+ end
+ if opts.all then
+ e2lib.abort("--all and --playground are mutually exclusive")
+ end
+ if #opts.arguments ~= 1 then
+ e2lib.abort("please select one single result for the playground")
+ end
+end
+local force_rebuild = opts["force-rebuild"]
+local request_buildno = opts["request-buildno"]
+local keep_chroot = opts["keep"]
+
+-- apply flags to the selected results
+rc, re = e2tool.select_results(info, results, force_rebuild, request_buildno,
+ keep_chroot, build_mode, playground)
+if not rc then
+ e2lib.abort(re)
+end
+
+-- a list of results to build, topologically sorted
+local sel_res = {}
+if #results > 0 then
+ sel_res = e2tool.dlist_recursive(info, results)
+else
+ sel_res = e2tool.dsort(info)
+end
+
+rc, re = e2tool.print_selection(info, sel_res)
+if not rc then
+ e2lib.abort(re)
+end
+
+if opts.release and not e2tool.e2_has_fixed_tag(info) then
+ e2lib.abort("Failure: e2 is on pseudo tag while building in release mode.")
+end
+-- XXX e2hook.run_hook(info, "pre-build", a, "e2-build")
+
+if opts["buildnumber"] then
+ e2lib.logf(1, "setting up build numbers")
+ local rc, re
+ rc, re = e2tool.buildnumber_read(info)
+ if not rc then
+ e2lib.abort(re)
+ end
+ rc, re = e2tool.buildnumber_mergetoresults(info)
+ if not rc then
+ e2lib.abort(re)
+ end
+end
+
+-- calculate build ids ids
+e2tool.calc_buildids(info)
+
+if opts["buildid"] then
+ for _,r in ipairs(sel_res) do
+ print(string.format("%-20s [%s]", r, e2tool.buildid(info, r)))
+ end
+ e2lib.finish()
+end
+
+-- build
+local rc, re = e2build.build_results(info, sel_res)
+if not rc then
+ e2lib.abort(re)
+end
+-- XXX e2hook.run_hook(info, "post-build", a, "e2-build")
+e2hook.run_hook(info, "tool-finish", nil, "e2-build")
+e2lib.finish()
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2-cf -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2 cf <command> ...
+
+commands:
+ newresult <name>
+ newsource <name> <scm>
+ editresult <name>
+ editbuildscript <name>
+ editsource <name>
+
+Commands starting with 'edit' can be abbreviated by using e...
+Commands starting with 'new' can be abbreviated by using n...
+Commands can be shortened as long as they remain unambiguous
+
+e.g.: eb <name> is equivalent to editbuildscript <name>
+
+modify and create configuration files
+]]
+
+local opts = e2option.parse(arg)
+
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+local rc, re = e2tool.check_project_info(info)
+if not rc then
+ e2lib.abort(re)
+end
+
+rc, re = e2lib.chdir(info.root)
+if not rc then
+ e2lib.abort(re)
+end
+
+local editor = os.getenv("EDITOR")
+if not editor then
+ editor = "vi"
+end
+
+local commands = {}
+
+local function newsource(info, ...)
+ local e = new_error("newsource")
+ local t = ...
+ local name = t[2]
+ local scm = t[3]
+ if not name then
+ e:append("missing parameter: name")
+ end
+ if not scm then
+ e:append("missing parameter: scm")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ local cfdir = string.format("src/%s", name)
+ local cf = string.format("src/%s/config", name)
+ local cftemplate = string.format("%s/source.%s", info.local_template_path,
+ scm)
+ if not e2lib.isfile(cftemplate) then
+ return false, e:append("template not available:", cftemplate)
+ end
+ if not e2lib.isfile(cf) and e2lib.isfile(cftemplate) then
+ local rc, re = e2lib.mkdir(cfdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2lib.cp(cftemplate, cf)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ rc, re = commands.editsource(info, ...)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+local function editsource(info, ...)
+ local e = new_error("editsource")
+ local t = ...
+ local name = t[2]
+ if not name then
+ e:append("missing parameter: name")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ local cf = string.format("src/%s/config", name)
+ rc = os.execute(string.format("%s %s", editor, cf))
+ return true, nil
+end
+
+local function newresult(info, ...)
+ local e = new_error("newresult")
+ local t = ...
+ local name = t[2]
+ if not name then
+ e:append("missing parameter: name")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ local cfdir = string.format("res/%s", name)
+ local cf = string.format("res/%s/config", name)
+ local bs = string.format("res/%s/build-script", name)
+ local cftemplate = string.format("%s/result", info.local_template_path)
+ local bstemplate = string.format("%s/build-script", info.local_template_path)
+ if not e2lib.isfile(cf) and not e2lib.isfile(bs) and
+ e2lib.isfile(cftemplate) and e2lib.isfile(bstemplate) then
+ local rc, re = e2lib.mkdir(cfdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2lib.cp(cftemplate, cf)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2lib.cp(bstemplate, bs)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ rc, re = commands.editresult(info, ...)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = commands.editbuildscript(info, ...)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+local function editresult(info, ...)
+ local e = new_error("editresult")
+ local t = ...
+ local name = t[2]
+ if not name then
+ e:append("missing parameter: name")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ local cf = string.format("res/%s/config", name)
+ os.execute(string.format("%s %s", editor, cf))
+ return true, nil
+end
+
+local function editbuildscript(info, ...)
+ local e = new_error("editbuildscript")
+ local t = ...
+ local name = t[2]
+ if not name then
+ e:append("missing parameter: name")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ local cf = string.format("res/%s/build-script", name)
+ os.execute(string.format("%s %s", editor, cf))
+ return true, nil
+end
+
+commands.editbuildscript = editbuildscript
+commands.editresult = editresult
+commands.newresult = newresult
+commands.newsource = newsource
+commands.editsource = editsource
+commands.ebuildscript = editbuildscript
+commands.eresult = editresult
+commands.nresult = newresult
+commands.nsource = newsource
+commands.esource = editsource
+
+local i = 1
+local match = {}
+local cmd = opts.arguments[1]
+if #opts.arguments < 1 then
+ e2option.usage()
+ e2lib.finish(1)
+end
+for c,f in pairs(commands) do
+ if c:match(string.format("^%s", cmd)) then
+ table.insert(match, c)
+ end
+end
+if #match == 1 then
+ local a = {}
+ for _,o in ipairs(opts.arguments) do
+ table.insert(a, o)
+ end
+ local f = commands[match[1]]
+ rc, re = f(info, a)
+ if not rc then
+ e2lib.abort(re)
+ end
+else
+ if #match > 1 then
+ print(string.format("Ambiguous command: %s", cmd))
+ end
+ print("Available commands:")
+ for c,f in pairs(commands) do
+ print(c)
+ end
+ e2lib.finish(1)
+end
+e2lib.finish(0)
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- config.lua -*- lua -*-
+--
+-- Program configuration
+
+E2_VERSION = "@E2_VERSION@"
+E2_COMMIT = "@E2_COMMIT@"
+E2_PREFIX = "@E2_PREFIX@"
+E2_SYNTAX = "@E2_SYNTAX@"
+
+require("e2util_local")
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- cvs.lua - CVS-specific SCM operations -*- Lua -*-
+--
+-- See e2scm.lua for more information about these operations.
+--
+-- A "cvs" source description file "src" has this content:
+--
+-- <server> <remote> <working> <branch> <tag>
+--
+-- Restrictions:
+--
+-- * the working copy directory must have the same basename as
+-- the CVS module name
+-- * branches are not supported, yet
+--
+-- This code has only been tested superficially.
+
+
+local cvs = {}
+
+--- validate source configuration, log errors to the debug log
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+function cvs.validate_source(info, sourcename)
+ local rc, re = generic_validate_source(info, sourcename)
+ if not rc then
+ -- error in generic configuration. Don't try to go on.
+ return false, re
+ end
+ local src = info.sources[ sourcename ]
+ if not src.sourceid then
+ src.sourceid = {}
+ end
+ local e = new_error("in source %s:", sourcename)
+ rc, re = source_apply_default_working(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ e:setcount(0)
+ -- XXX should move the default value out of the validate function
+ if not src.server then
+ e:append("source has no `server' attribute")
+ end
+ if not src.licences then
+ e:append("source has no `licences' attribute")
+ end
+ if not src.cvsroot then
+ e2lib.warnf("WDEFAULT", "in source %s:", sourcename)
+ e2lib.warnf("WDEFAULT",
+ " source has no `cvsroot' attribute, defaulting to the server path")
+ src.cvsroot = "."
+ end
+ if not src.cvsroot then
+ e:append("source has no `cvsroot' attribute")
+ end
+ if src.remote then
+ e:append("source has `remote' attribute, not allowed for cvs sources")
+ end
+ if not src.branch then
+ e:append("source has no `branch' attribute")
+ end
+ if not type(src.tag) == "string" then
+ e:append("source has no `tag' attribute or tag attribute has wrong type")
+ end
+ if not src.module then
+ e:append("source has no `module' attribute")
+ end
+ if not src.working then
+ e:append("source has no `working' attribute")
+ end
+ local rc, re = transport.check_tool("cvs")
+ if not rc then
+ e:cat(re)
+ end
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- build the cvsroot string
+-- @param u url table
+-- @return string: cvsroot, nil on error
+-- @return an error object on failure
+local function mkcvsroot(u)
+ local cvsroot = nil
+ if u.transport == "file" then
+ cvsroot = string.format("/%s", u.path)
+ elseif (u.transport == "ssh") or
+ (u.transport == "rsync+ssh") then
+ cvsroot = string.format("%s:/%s", u.server, u.path)
+ elseif u.transport == "cvspserver" then
+ cvsroot = string.format(":pserver:%s:/%s", u.server, u.path)
+ else
+ return nil, new_error("cvs: transport not supported")
+ end
+ return cvsroot, nil
+end
+
+--- build the revision string containing branch or tag name
+-- @param src table: source table
+-- @param source_set string: source set
+-- @return string: cvsroot, nil on error
+-- @return an error object on failure
+local function mkrev(src, source_set)
+ local rev = nil
+ if source_set == "branch" or
+ (source_set == "lazytag" and src.tag == "^") then
+ rev = src.branch
+ elseif (source_set == "tag" or source_set == "lazytag") and
+ src.tag ~= "^" then
+ rev = src.tag
+ end
+ if not rev then
+ return nil, new_error("source set not allowed")
+ end
+ return rev, nil
+end
+
+function cvs.fetch_source(info, sourcename)
+ local rc, re = cvs.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local e = new_error("fetching source failed: %s", sourcename)
+ local src = info.sources[ sourcename ]
+ local location = src.cvsroot
+ local server = src.server
+ local surl, re = cache.remote_url(info.cache, server, location)
+ if not surl then
+ return false, e:cat(re)
+ end
+ local u, re = url.parse(surl)
+ if not u then
+ return false, e:cat(re)
+ end
+ local cmd = nil
+ local cvsroot, re = mkcvsroot(u)
+ if not cvsroot then
+ return false, e:cat(re)
+ end
+ -- always fetch the configured branch, as we don't know the build mode here.
+ local rev = src.branch
+ local rsh = transport.get_tool("ssh")
+ local cvstool = transport.get_tool("cvs")
+ local cvsflags = transport.get_tool_flags("cvs")
+ -- split the working directory into dirname and basename as some cvs clients
+ -- don't like slashes (e.g. in/foo) in their checkout -d<path> argument
+ local dir = e2lib.dirname(src.working)
+ local base = e2lib.basename(src.working)
+ -- cd info.root && cvs -d cvsroot checkout -R [-r rev] -d working module
+ if rev == "HEAD" then
+ -- HEAD is a special case in cvs: do not pass -r 'HEAD' to cvs checkout
+ rev = ""
+ else
+ rev = string.format("-r '%s'", rev)
+ end
+ cmd = string.format(
+ "cd \"%s/%s\" && " ..
+ "CVS_RSH=\"%s\" " ..
+ "%s %s -d \"%s\" checkout -R %s -d \"%s\" \"%s\"",
+ info.root, dir, rsh,
+ cvstool, cvsflags, cvsroot,
+ rev, base, src.module)
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+function cvs.prepare_source(info, sourcename, source_set, buildpath)
+ local rc, re = cvs.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local e = new_error("cvs.prepare_source failed")
+ local src = info.sources[ sourcename ]
+ local location = src.cvsroot
+ local server = src.server
+ local surl, re = cache.remote_url(info.cache, server, location)
+ if not surl then
+ return false, e:cat(re)
+ end
+ local u, re = url.parse(surl)
+ if not u then
+ return false, e:cat(re)
+ end
+ local cvsroot, re = mkcvsroot(u) -- XXX error checking
+ if not cvsroot then
+ return false, re
+ end
+ local cmd = nil
+ if source_set == "tag" or source_set == "branch" then
+ local rev = mkrev(src, source_set)
+ local rsh = transport.get_tool("ssh")
+ local cvstool = transport.get_tool("cvs")
+ local cvsflags = transport.get_tool_flags("cvs")
+ -- cd buildpath && cvs -d cvsroot export -R -r rev module
+ cmd = string.format(
+ "cd \"%s\" && " ..
+ "CVS_RSH=\"%s\" " ..
+ "%s %s -d \"%s\" export -R -r \"%s\" -d \"%s\" \"%s\"",
+ buildpath, rsh,
+ cvstool, cvsflags, cvsroot, rev, src.name, src.module)
+ elseif source_set == "working-copy" then
+ -- cp -R info.root/src.working buildpath
+ cmd = string.format(
+ "cp -R \"%s/%s\" \"%s/%s\"",
+ info.root, src.working, buildpath, src.name)
+ else
+ e2lib.abort("invalid build mode")
+ end
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+function cvs.update(info, sourcename)
+ local rc, re = cvs.validate_source(info, sourcename)
+ if not rc then
+ e2lib.abort(re)
+ end
+ local e = new_error("updating cvs source failed")
+ local src = info.sources[ sourcename ]
+ local working = string.format("%s/%s", info.root, src.working)
+ local rsh = transport.get_tool("ssh")
+ local cvstool = transport.get_tool("cvs")
+ local cvsflags = transport.get_tool_flags("cvs")
+ local cmd = string.format(
+ "cd \"%s\" && " ..
+ "CVS_RSH=\"%s\" " ..
+ "%s %s update -R",
+ working, rsh, cvstool, cvsflags)
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ e:cat(re)
+ return false, e
+ end
+ return true, nil
+end
+
+function cvs.working_copy_available(info, sourcename)
+ local rc, e
+ rc, e = cvs.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ local src = info.sources[sourcename]
+ local dir = string.format("%s/%s", info.root, src.working)
+ return e2lib.isdir(dir)
+end
+
+function cvs.has_working_copy(info, sourcename)
+ return true
+end
+
+--- create a table of lines for display
+-- @param info the info structure
+-- @param sourcename string
+-- @return a table, nil on error
+-- @return an error object on failure
+function cvs.display(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re
+ rc, re = cvs.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local display = {}
+ display[1] = string.format("type = %s", src.type)
+ display[2] = string.format("branch = %s", src.branch)
+ display[3] = string.format("tag = %s", src.tag)
+ display[4] = string.format("server = %s", src.server)
+ display[5] = string.format("cvsroot = %s", src.cvsroot)
+ display[6] = string.format("module = %s", src.module)
+ display[7] = string.format("working = %s", src.working)
+ local i = 8
+ for _,l in ipairs(src.licences) do
+ display[i] = string.format("licence = %s", l)
+ i = i + 1
+ end
+ for k,v in pairs(src.sourceid) do
+ if v then
+ display[i] = string.format("sourceid [%s] = %s", k, v)
+ i = i + 1
+ end
+ end
+ return display, nil
+end
+
+function cvs.sourceid(info, sourcename, source_set)
+ local src = info.sources[sourcename]
+ local rc, re
+ rc, re = cvs.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ if source_set == "working-copy" then
+ src.sourceid[source_set] = "working-copy"
+ end
+ if src.sourceid[source_set] then
+ return src.sourceid[source_set]
+ end
+ local e = new_error("calculating sourceid failed for source %s",
+ sourcename)
+ local hc = hash.hash_start()
+ hash.hash_line(hc, src.name)
+ hash.hash_line(hc, src.type)
+ for _,l in ipairs(src.licences) do
+ hash.hash_line(hc, l)
+ local licenceid, re = e2tool.licenceid(info, l)
+ if not licenceid then
+ return nil, re
+ end
+ hash.hash_line(hc, licenceid)
+ end
+ -- cvs specific
+ if source_set == "tag" and src.tag ~= "^" then
+ -- we rely on tags being unique with cvs
+ hc:hash_line(src.tag)
+ else
+ -- the old function took a hash of the CVS/Entries file, but
+ -- forgot the subdirecties' CVS/Entries files. We might
+ -- reimplement that once...
+ e:append("cannot calculate sourceid for source set %s",
+ source_set)
+ return false, e
+ end
+ hash.hash_line(hc, src.server)
+ hash.hash_line(hc, src.cvsroot)
+ hash.hash_line(hc, src.module)
+ -- skip src.working
+ e2lib.logf(4, "hash data for source %s\n%s", src.name, hc.data)
+ src.sourceid[source_set] = hash.hash_finish(hc)
+ return src.sourceid[source_set], nil
+end
+
+function cvs.toresult(info, sourcename, sourceset, directory)
+ -- <directory>/source/<sourcename>.tar.gz
+ -- <directory>/makefile
+ -- <directory>/licences
+ local rc, re
+ local e = new_error("converting result")
+ rc, re = check(info, sourcename, true)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local src = info.sources[sourcename]
+ -- write makefile
+ local makefile = "makefile"
+ local source = "source"
+ local sourcedir = string.format("%s/%s", directory, source)
+ local archive = string.format("%s.tar.gz", sourcename)
+ local fname = string.format("%s/%s", directory, makefile)
+ rc, re = e2lib.mkdir(sourcedir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local f, msg = io.open(fname, "w")
+ if not f then
+ return false, e:cat(msg)
+ end
+ f:write(string.format(
+ ".PHONY:\tplace\n\n"..
+ "place:\n"..
+ "\ttar xzf \"%s/%s\" -C \"$(BUILD)\"\n",
+ source, archive))
+ f:close()
+ -- export the source tree to a temporary directory
+ local tmpdir = e2lib.mktempdir()
+ rc, re = cvs.prepare_source(info, sourcename, sourceset, tmpdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- create a tarball in the final location
+ local archive = string.format("%s.tar.gz", src.name)
+ local tar_args = string.format("-C '%s' -czf '%s/%s' '%s'",
+ tmpdir, sourcedir, archive, sourcename)
+ rc, re = e2lib.tar(tar_args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- write licences
+ local destdir = string.format("%s/licences", directory)
+ local fname = string.format("%s/%s.licences", destdir, archive)
+ local licence_list = table.concat(src.licences, "\n") .. "\n"
+ rc, re = e2lib.mkdir(destdir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.write_file(fname, licence_list)
+ if not rc then
+ return false, e:cat(re)
+ end
+ e2lib.rmtempdir(tmpdir)
+ return true, nil
+end
+
+function cvs.check_workingcopy(info, sourcename)
+ local rc, re
+ local e = new_error("checking working copy failed")
+ e:append("in source %s (cvs configuration):", sourcename)
+ e:setcount(0)
+ rc, re = cvs.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local src = info.sources[sourcename]
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+e2scm.register("cvs", cvs)
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- dlist - list sorted dependencies -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-dlist [<option> | <result> ...]
+
+for the given result show those results which it depends on
+]]
+
+e2option.flag("recursive", "show indirect dependencies, too")
+local opts = e2option.parse(arg)
+
+if #opts.arguments == 0 then
+ e2lib.abort("no result given - enter `e2-dlist --help' for usage information")
+elseif #opts.arguments ~= 1 then e2option.usage(1) end
+
+local result = opts.arguments[1]
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-dlist")
+
+if not info.results[ result ] then
+ e2lib.abort("no such result: ", result)
+end
+
+local dep = opts.recursive
+ and e2tool.dlist_recursive(info, result)
+ or e2tool.dlist(info, result)
+
+if dep then
+ for i = 1, #dep do print(dep[i]) end
+end
+
+e2hook.run_hook(info, "tool-finish", nil, "e2-dlist")
+e2lib.finish()
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- dsort -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-dsort
+
+lists all results sorted by dependency
+]]
+
+e2option.parse(arg)
+
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-dsort")
+
+local d = e2tool.dsort(info)
+if d then
+ for i = 1, #d do print(d[i]) end
+end
+
+e2hook.run_hook(info, "tool-finish", nil, "e2-dsort")
+e2lib.finish()
+
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ e2api.c
+
+ C-API for accessing project information
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "e2api.h"
+
+
+static char *error_message = NULL;
+
+
+lua_State *
+e2_init(char *project_path)
+{
+ char rpath[ PATH_MAX + 1 ];
+ char buffer[ PATH_MAX + 1 ];
+ lua_State *lua;
+
+ if(realpath(project_path, rpath) == NULL) {
+ free(error_message);
+ error_message = strdup(strerror(errno));
+ return NULL;
+ }
+
+ strcpy(buffer, rpath);
+ strcat(buffer, "/.e2/lib/e2/?.lc;");
+ strcat(buffer, rpath);
+ strcat(buffer, "/.e2/lib/e2/?.lua");
+ setenv("LUA_PATH", buffer, 1);
+ strcpy(buffer, rpath);
+ strcat(buffer, "/.e2/lib/e2/?.so");
+ setenv("LUA_CPATH", buffer, 1);
+ lua = lua_open();
+ luaL_openlibs(lua);
+ lua_newtable(lua);
+ lua_setglobal(lua, "arg");
+ lua_pushstring(lua, rpath);
+ lua_setglobal(lua, "e2api_rpath");
+ strcpy(buffer, rpath);
+ strcat(buffer, "/.e2/lib/e2/e2local.lc");
+
+ if(luaL_loadfile(lua, buffer) != 0) {
+ free(error_message);
+ error_message = strdup(lua_tostring(lua, -1));
+ lua_close(lua);
+ return NULL;
+ }
+
+ if(lua_pcall(lua, 0, 0, 0) != 0) {
+ free(error_message);
+ error_message = strdup(lua_tostring(lua, -1));
+ lua_close(lua);
+ return NULL;
+ }
+
+ return lua;
+}
+
+
+void
+e2_exit(lua_State *lua)
+{
+ free(error_message);
+ lua_close(lua);
+}
+
+
+static int
+exit_handler(lua_State *lua)
+{
+ free(error_message);
+ error_message = strdup(lua_tostring(lua, -1));
+ return luaL_error(lua, error_message);
+}
+
+
+int
+e2_info(lua_State *lua)
+{
+ lua_getglobal(lua, "e2lib");
+ lua_pushstring(lua, "abort_with_message");
+ lua_pushcfunction(lua, exit_handler);
+ lua_rawset(lua, -3);
+ lua_getglobal(lua, "e2tool");
+ lua_pushstring(lua, "collect_project_info");
+ lua_rawget(lua, -2);
+ lua_remove(lua, -2); /* remove e2tool table */
+ lua_getglobal(lua, "e2api_rpath");
+
+ if(lua_pcall(lua, 1, 1, 0) != 0) {
+ free(error_message);
+ error_message = strdup(lua_tostring(lua, -1));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+char *
+e2_error(void)
+{
+ return error_message;
+}
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ e2api.h
+*/
+
+
+#ifndef E2API_H
+#define E2API_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+
+ extern lua_State *e2_init(char *project_path);
+ extern void e2_exit(lua_State *state);
+ extern int e2_info(lua_State *state);
+ extern char *e2_error(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2api.lua
+-- Use e2 from a Lua program.
+
+
+e2api = {}
+
+function e2api.init(project_path)
+ package.path = project_path .. "/.e2/lib/e2/?.lc;" ..
+ project_path .. "/.e2/lib/e2/?.lua;" .. package.path
+ package.cpath = project_path .. "/.e2/lib/e2/?.so;" .. package.cpath
+ e2api.rpath = project_path
+ require("e2local")
+ e2lib.abort_with_message = error
+end
+
+function e2api.info()
+ return e2tool.collect_project_info(e2api.rpath)
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2build.lua -*- Lua -*-
+
+
+e2build = e2lib.module("e2build")
+
+function e2build.remove_logfile(info, r, return_flags)
+ local res = info.results[r]
+ local rc = os.remove(res.build_config.buildlog)
+ -- always return true
+ return true, nil
+end
+
+--- cache a result
+-- @param info
+-- @param server
+-- @param location
+-- @return bool
+-- @return an error object on failure
+function e2build.cache_result(info, server, location)
+ return result.fetch(info.cache, server, location)
+end
+
+-- return true if the result given in c is already available, false otherwise
+-- return the path to the result
+-- check if a result is already available
+-- @param info
+-- @param r string: result name
+-- @param return_flags table: return values through this table
+-- @return bool
+-- @return an error object on failure
+function e2build.result_available(info, r, return_flags)
+ e2lib.log(4, string.format("e2build.result_available(%s)", tostring(r)))
+ local res = info.results[r]
+ local mode = res.build_mode
+ local buildid = res.build_mode.buildid(e2tool.buildid(info, r))
+ local sbid = e2tool.bid_display(buildid)
+ local rc, re
+ local e = new_error("error while checking if result is available: %s", r)
+ if res.playground then
+ return_flags.message = string.format("building %-20s [%s] [playground]",
+ r, sbid)
+ return_flags.stop = false
+ return true, nil
+ end
+ if res.build_mode.source_set() == "working-copy" then
+ return_flags.message = string.format("building %-20s [%s]", r, sbid)
+ return_flags.stop = false
+ return true, nil
+ end
+ local server, location = mode.storage(info.project_location, info.release_id)
+ local dep_set = mode.buildid(e2tool.buildid(info, r))
+ -- cache the result
+ local result_location = string.format("%s/%s/%s/result.tar", location, r,
+ dep_set)
+ local cache_flags = {}
+ rc, re = cache.cache_file(info.cache, server, result_location, cache_flags)
+ if not rc then
+ e2lib.log(3, "caching result failed")
+ -- ignore
+ end
+ local cache_flags = {}
+ local path, re = cache.file_path(info.cache, server, result_location,
+ cache_flags)
+ rc = e2lib.isfile(path)
+ if not rc then
+ -- result is not available. Build.
+ return_flags.message = string.format("building %-20s [%s]", r, sbid)
+ return_flags.stop = false
+ return true, nil
+ end
+ e2lib.log(3, "result is available locally")
+--[[
+ rc, re = e2build.update_result_timestamp(info, server, location)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- and push the updated metadata to the server again, if the result
+ -- exists on the server.
+]]
+ rc, re = e2build.linklast(info, r, return_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- return true
+ return_flags.message = string.format("skipping %-20s [%s]", r, sbid)
+ return_flags.stop = true
+ return true, nil
+end
+
+function e2build.update_result_timestamp(info, server, location)
+ -- update the timestamp
+ sr:update_timestamp()
+ local rc, re = sr:write(rp)
+ if not rc then
+ -- can't write back metadata, do not continue.
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+--- build config
+-- @class table
+-- @name build config
+-- @field mode table: the build mode policy
+-- @field release id string: the release name
+-- @field info table: the info table
+-- @field c string: path to the chroot
+-- @field T string: absolute path to the temporary build directory
+-- inside chroot
+-- @field Tc string: same as c.T but relative to c
+-- @field strict bool: pseudo tag "^" not allowed when true
+-- @field r string: result name
+-- @field buildlog string: build log file
+-- @field buildid string: build id
+-- @field groups table of strings: chroot groups
+
+--- generate build_config and store in res.build_config
+-- @param info
+-- @param r string: result name
+-- @return bool
+-- @return an error object on failure
+function e2build.build_config(info, r)
+ e2lib.log(4, string.format("e2build.build_config(%s, %s)",
+ tostring(info), tostring(r)))
+ local e = new_error("setting up build configuration for result `%s' failed",
+ r)
+ local res = info.results[r]
+ if not res then
+ return false, e:append("no such result: %s", r)
+ end
+ local buildid, re = e2tool.buildid(info, r)
+ if not buildid then
+ return false, e:cat(re)
+ end
+ res.build_config = {} -- build up a new build config
+ local tab = res.build_config
+ local tmpdir = string.format("%s/e2-2.2-build/%s", e2lib.tmpdir,
+ e2lib.username)
+ local project = info.name
+ local builddir = "tmp/e2"
+ tab.mode = nil -- XXX
+ tab.location = nil -- XXX info.project_location
+ tab.release_id = nil -- XXX release_id
+ tab.c = string.format("%s/%s/%s/chroot", tmpdir, project, r)
+ tab.T = string.format("%s/%s/%s/chroot/%s", tmpdir, project, r, builddir)
+ tab.Tc = string.format("/%s", builddir)
+ tab.r = string.format("%s", r)
+ tab.chroot_call_prefix = info.chroot_call_prefix[info.project.chroot_arch]
+ tab.buildlog = string.format("%s/log/build.%s.log", info.root, r)
+ tab.scriptdir = "script"
+ tab.build_driver = ""
+ tab.build_driver_file = string.format("build-driver")
+ tab.buildrc_file = string.format("buildrc")
+ tab.buildrc_noinit_file = string.format("buildrc-noinit")
+ tab.profile = string.format("/tmp/bashrc")
+ tab.builtin_env = {
+ E2_BUILD_NUMBER = res.buildno,
+ E2_TMPDIR = res.build_config.Tc,
+ E2_RESULT = r,
+ E2_RELEASE_ID = info.project.release_id,
+ E2_PROJECT_NAME = info.project.name,
+ E2_BUILDID = buildid,
+ T = res.build_config.Tc,
+ r = r,
+ R = r,
+ }
+ e2lib.logf(4, "build config for result %s: ", r)
+ for k,v in pairs(tab) do
+ v = tostring(v)
+ e2lib.log(4, string.format("\t%-10s = %s", k, v))
+ end
+ tab.groups = {}
+ for _,g in ipairs(res.chroot) do
+ tab.groups[g] = true
+ end
+ return tab
+end
+
+function e2build.setup_chroot(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("error setting up chroot")
+ -- create the chroot path and create the chroot marker file without root
+ -- permissions. That makes sure we have write permissions here.
+ rc, re = e2lib.mkdir(res.build_config.c, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2lib.touch(res.build_config.c .. "/emlix-chroot")
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- e2-su set_permissions_2_2 <chroot_path>
+ local args = string.format("set_permissions_2_2 '%s'",
+ res.build_config.c)
+ local rc, re = e2lib.e2_su_2_2(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ for _,grp in ipairs(info.chroot.groups) do
+ if res.build_config.groups[grp.name] then
+ for _, f in ipairs(grp.files) do
+ local flags = { cache = true }
+ local rc, re = cache.cache_file(info.cache, grp.server, f, flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local path, re = cache.file_path(info.cache, grp.server, f, flags)
+ if not path then
+ return false, e:cat(re)
+ end
+ local tartype
+ if path:match("tgz$") or path:match("tar.gz$") then
+ tartype = "tar.gz"
+ elseif path:match("tar.bz2$") then
+ tartype = "tar.bz2"
+ elseif path:match("tar$") then
+ tartype = "tar"
+ else
+ e:append("unknown archive type for chroot file: %s", path)
+ return false, e
+ end
+ -- e2-su extract_tar_2_2 <path> <tartype> <file>
+ local args = string.format("extract_tar_2_2 '%s' '%s' '%s'",
+ res.build_config.c, tartype, path)
+ local rc, re = e2lib.e2_su_2_2(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ end
+ end
+ return true, nil
+end
+
+function e2build.enter_playground(info, r, chroot_command)
+ if not chroot_command then
+ chroot_command = "/bin/bash"
+ end
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("entering playground")
+ e2lib.log(4, "entering playground for " .. r .. " ...")
+ local term = e2lib.terminal
+ local e2_su = transport.get_tool("e2-su-2.2")
+ local cmd = string.format("%s %s chroot_2_2 '%s' %s",
+ res.build_config.chroot_call_prefix, e2_su,
+ res.build_config.c, chroot_command)
+ os.execute(cmd)
+ -- return code depends on user commands. Ignore.
+ return true, nil
+end
+
+function e2build.fix_permissions(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("fixing permissions failed")
+ e2lib.log(3, "fix permissions")
+ local args = string.format("chroot_2_2 '%s' chown -R root:root '%s'",
+ res.build_config.c, res.build_config.Tc)
+ rc, re = e2lib.e2_su_2_2(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local args = string.format("chroot_2_2 '%s' chmod -R u=rwX,go=rX '%s'",
+ res.build_config.c, res.build_config.Tc)
+ rc, re = e2lib.e2_su_2_2(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+function e2build.playground(info, r, return_flags)
+ local res = info.results[r]
+ if res.playground then
+ return_flags.message = string.format("playground done for: %-20s", r)
+ return_flags.stop = true
+ return true, nil
+ end
+ -- do nothing...
+ return true, nil
+end
+
+function e2build.runbuild(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("build failed")
+ e2lib.log(3, "building " .. r .. " ...")
+ local runbuild = string.format("/bin/bash -e -x '%s/%s/%s'",
+ res.build_config.Tc, res.build_config.scriptdir,
+ res.build_config.build_driver_file)
+ local e2_su = transport.get_tool("e2-su-2.2")
+ local cmd = string.format("%s %s chroot_2_2 '%s' %s",
+ res.build_config.chroot_call_prefix, e2_su,
+ res.build_config.c, runbuild)
+ -- the build log is written to an external logfile
+ local out = luafile.open(res.build_config.buildlog, "w")
+ local function logto(output)
+ e2lib.log(3, output)
+ out:write(output)
+ out:flush()
+ end
+ local rc = e2lib.callcmd_capture(cmd, logto)
+ out:close()
+ if rc ~= 0 then
+ -- XXX e2hook.run_hook(c.info, "build-failure", c, "e2-build")
+ e = new_error("build script for %s failed with exit status %d", r, rc)
+ e:append("see %s for more information", res.build_config.buildlog)
+ return false, e
+ end
+ -- XXX e2hook.run_hook(c.info, "build-post-runbuild", c, "e2-build")
+ return true, nil
+end
+
+function e2build.chroot_cleanup(info, r, return_flags)
+ local res = info.results[r]
+ local e = new_error("cleaning up chroot failed")
+ if res.keep_chroot then
+ return true, nil
+ end
+ local args = string.format("remove_chroot_2_2 '%s'", res.build_config.c)
+ local rc, re = e2lib.e2_su_2_2(args)
+ if not rc then
+ return e:cat(re)
+ end
+ local f = string.format("%s/playground", info.root)
+ local s = e2util.stat(f)
+ if s and s.type == "symbolic-link" then
+ local rc, e = e2lib.rm(f, "-f")
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+end
+
+function e2build.chroot_cleanup_if_exists(info, r, return_flags)
+ local res = info.results[r]
+ if e2build.chroot_exists(info, r, return_flags) then
+ return e2build.chroot_cleanup(info, r, return_flags)
+ end
+ return true, nil
+end
+
+--- check if a chroot exists for this result
+-- @param info
+-- @param r string: result name
+-- @return bool
+function e2build.chroot_exists(info, r)
+ local res = info.results[r]
+ local f = string.format("%s/emlix-chroot", res.build_config.c)
+ return e2lib.isfile(f)
+end
+
+function e2build.sources(info, r, return_flags)
+ local e = new_error("installing sources")
+ local i, k, l, source, cp
+
+ -- the development build case
+ --
+ -- install directory structure
+ -- install build-script
+ -- install e2-runbuild
+ -- install build time dependencies
+ --
+ -- for each source do
+ -- prepare_source
+ -- end
+
+ local function append_to_build_driver(info, r, script)
+ local res = info.results[r]
+ res.build_config.build_driver =
+ res.build_config.build_driver .. string.format("%s\n", script)
+ end
+
+ local function install_directory_structure(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("installing directory structure")
+ local dirs = {"out", "init", "script", "build", "root", "env", "dep"}
+ for _, v in pairs(dirs) do
+ local d = string.format("%s/%s", res.build_config.T, v)
+ local rc, re = e2lib.mkdir(d, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+ end
+
+ local function install_build_script(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("installing build script")
+ local location = string.format("res/%s/build-script", r)
+ local destdir = string.format("%s/script", res.build_config.T)
+ rc, re = transport.fetch_file(info.root_server, location, destdir, nil)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+ end
+
+ local function install_env(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("installing environment files failed")
+ -- install builtin environment variables
+ local file = string.format("%s/env/builtin", res.build_config.T)
+ rc, re = write_environment_script(res.build_config.builtin_env, r, file)
+ if not rc then
+ return false, e:cat(re)
+ end
+ append_to_build_driver(info, r, string.format("source %s/env/builtin",
+ res.build_config.Tc))
+ -- install project specific environment variables
+ local file = string.format("%s/env/env", res.build_config.T)
+ rc, re = write_environment_script(info.env, r, file)
+ if not rc then
+ return false, e:cat(re)
+ end
+ append_to_build_driver(info, r, string.format("source %s/env/env",
+ res.build_config.Tc))
+ return true, nil
+ end
+
+ local function install_init_files(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("installing init files")
+ for x in e2lib.directory(info.root .. "/proj/init") do
+ if not e2lib.is_backup_file(x) then
+ local location = string.format("proj/init/%s", x)
+ local destdir = string.format("%s/init", res.build_config.T)
+ rc, re = transport.fetch_file(info.root_server, location, destdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ append_to_build_driver(info, r, string.format("source %s/init/%s",
+ res.build_config.Tc, x))
+ end
+ end
+ return true, nil
+ end
+
+ local function install_build_driver(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("writing build driver script failed")
+ local bc = res.build_config
+ local destdir = string.format("%s/%s", bc.T, bc.scriptdir)
+ rc, re = write_build_driver(info, r, destdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+ end
+
+ local function install_build_time_dependencies(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("installing build time dependencies")
+ e2lib.log(3, string.format("install_build_time_dependencies"))
+ local deps
+ deps = e2tool.dlist(info, r)
+ for i, dep in pairs(deps) do
+ local tmpdir = e2lib.mktempdir()
+ local e = new_error("installing build time dependency failed: %s", dep)
+ local d = info.results[dep]
+ local buildid = e2tool.buildid(info, dep)
+
+ local dep_set = res.build_mode.dep_set(buildid)
+ local server, location = res.build_mode.storage(info.project_location,
+ info.release_id)
+ e2lib.log(3, string.format("searching for dependency %s in %s:%s",
+ dep, server, location))
+ local location1 = string.format("%s/%s/%s/result.tar", location, dep,
+ dep_set)
+ local cache_flags = {}
+ local rc, re = cache.fetch_file(info.cache, server, location1, tmpdir,
+ nil, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.chdir(tmpdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.mkdir("result")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.tar("-xf result.tar -C result")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.chdir("result")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.call_tool("sha1sum", "-c checksums")
+ if not rc then
+ e:append("checksum mismatch in dependency: %s", dep)
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.chdir("files")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local destdir = string.format("%s/dep/%s", res.build_config.T, dep)
+ rc, re = e2lib.mkdir(destdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ for f in e2lib.directory(".") do
+ rc, re = e2lib.mv(f, destdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ rc, re = e2tool.lcd(info, ".")
+ if not rc then
+ return false, e:cat(re)
+ end
+ e2lib.rmtempdir(tmpdir)
+ end
+ return true, nil
+ end
+
+ local function install_sources(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("installing sources")
+ e2lib.log(3, "install sources")
+ for i, source in pairs(res.sources) do
+ local e = new_error("installing source failed: %s", source)
+ local destdir = string.format("%s/build", res.build_config.T)
+ local source_set = res.build_mode.source_set()
+ local rc, re = scm.prepare_source(info, source, source_set,
+ destdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+ end
+
+ local steps = {
+ --XXX e2hook.run_hook(c.info, "build-pre-sources", c, "e2-build")
+ install_directory_structure,
+ install_build_script,
+ install_env,
+ install_init_files,
+ install_build_driver,
+ install_build_time_dependencies,
+ install_sources,
+ --XXX e2hook.run_hook(c.info, "build-post-sources", c, "e2-build")
+ }
+ for _,f in ipairs(steps) do
+ local rflags = {}
+ local rc, re = f(info, r, rflags)
+ if not rc then
+ return false, re
+ end
+ end
+ return true, nil
+end
+
+function e2build.linklast(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("creating link to last results")
+ -- calculate the path to the result
+ local server, location = res.build_mode.storage(info.project_location,
+ info.release_id)
+ local location1 = string.format("%s/%s/%s", location, r,
+ res.build_mode.buildid(res.buildid))
+ local cache_flags = {
+ check_only = true
+ }
+ local dst, re = cache.file_path(info.cache, server, location1, cache_flags)
+ if not dst then
+ return false, e:cat(re)
+ end
+ -- create the last link
+ local lnk_location = string.format("out/%s/last", r)
+ local lnk, re = cache.file_path(info.cache, info.root_server_name,
+ lnk_location)
+ if not lnk then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.mkdir(e2lib.dirname(lnk), "-p") -- create the directory
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.rm(lnk, "-f") -- remove the old link
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.symlink(dst, lnk) -- create the new link
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+--- store the result
+-- @param info
+-- @param r string: result name
+-- @param return_flags table
+-- @return bool
+-- @return an error object on failure
+function e2build.store_result(info, r, return_flags)
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("fetching build results from chroot")
+ e2lib.log(4, string.format("e2build.store_result"))
+
+ -- create a temporary directory to build up the result
+ local tmpdir = e2lib.mktempdir()
+
+ -- build a stored result structure and store
+ local rfilesdir = string.format("%s/out", res.build_config.T)
+ rc, re = e2lib.chdir(tmpdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.mkdir("result result/files")
+ if not rc then
+ return false, e:cat(re)
+ end
+ for f in e2lib.directory(rfilesdir, false, true) do
+ e2lib.logf(3, "result file: %s", f)
+ local s = string.format("%s/%s", rfilesdir, f)
+ local d = "result/files"
+ rc, re = e2lib.cp(s, d)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ rc, re = e2lib.chdir("result")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local args = "files/* >checksums"
+ rc, re = e2lib.call_tool("sha1sum", args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.chdir("..")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local args = string.format("-cf result.tar -C result .")
+ rc, re = e2lib.tar(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local server, location = res.build_mode.storage(info.project_location,
+ info.release_id)
+ local buildid = res.build_mode.buildid(e2tool.buildid(info, r))
+ local sourcefile = string.format("%s/result.tar", tmpdir)
+ local location1 = string.format("%s/%s/%s/result.tar", location, r, buildid)
+ local cache_flags = {}
+ local rc, re = cache.push_file(info.cache, sourcefile, server, location1,
+ cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2tool.lcd(info, ".")
+ if not rc then
+ return false, e:cat(re)
+ end
+ e2lib.rmtempdir(tmpdir)
+ return true, nil
+end
+
+--- build a set of results
+-- @param info
+-- @param results table: list of results, sorted by dependencies
+-- @return bool
+-- @return an error object on failure
+function e2build.build_results(info, results)
+ e2lib.logf(3, "building results")
+ for _, r in ipairs(results) do
+ local e = new_error("building result failed: %s", r)
+ local flags = {}
+ local t1 = os.time()
+ local rc, re = e2build.build_result(info, r, flags)
+ if not rc then
+ -- do not insert an error message from this layer.
+ return false, e:cat(re)
+ end
+ local t2 = os.time()
+ local deltat = os.difftime(t2, t1)
+ e2lib.logf(3, "timing: result [%s] %d", r, deltat)
+ if flags.stop then
+ return true, nil
+ end
+ end
+ return true, nil
+end
+
+--- build a result
+-- @param info
+-- @param result string: result name
+-- @return bool
+-- @return an error object on failure
+function e2build.build_result(info, result, return_flags)
+ e2lib.logf(3, "building result: %s", result)
+ local res = info.results[result]
+ for _,fname in ipairs(res.build_mode.build_process) do
+ local f = e2build[fname]
+ --e2lib.logf(3, "running function: e2build.%s", fname)
+ local t1 = os.time()
+ local flags = {}
+ local rc, re = f(info, result, flags)
+ local t2 = os.time()
+ local deltat = os.difftime(t2, t1)
+ e2lib.logf(3, "timing: step: %s [%s] %d", fname, result, deltat)
+ if not rc then
+ -- do not insert an error message from this layer.
+ return false, re
+ end
+ if flags.message then
+ e2lib.log(2, flags.message)
+ end
+ if flags.stop then
+ -- stop the build process for this result
+ return true, nil
+ end
+ if flags.terminate then
+ -- stop the build process for this result and terminate
+ return true, nil
+ end
+ end
+ return true, nil
+end
+
+--- push a result to a server via cache
+-- build up the <result>/<buildid> structure relative to rlocation on the
+-- server
+-- @param c the cache
+-- @param path string: the path where the results are stored locally ("out/")
+-- @param rname string: the result name
+-- @param bid string: the buildid
+-- @param server string: the server name
+-- @param rlocation: the location to store the result on the server
+-- @return bool
+-- @return nil, an error string on error
+function e2build.push_result(c, path, rname, bid, server, rlocation)
+ -- read the result
+ local llocation = string.format("%s/%s/%s", path, rname, bid)
+ local sr, e = result.new()
+ sr, e = result.read(llocation)
+ if not sr then
+ return false, e
+ end
+ -- store each file to the server via cache
+ local flags = {}
+ flags.all = true
+ for _, file in pairs(sr:get_filelist(flags)) do
+ local sourcefile = string.format("%s/%s/%s/%s", path, rname, bid,
+ file.name)
+ local location = string.format("%s/%s/%s", rlocation, bid, file.name)
+ local cache_flags = {}
+ local rc, e = cache.push_file(c, sourcefile, server, location,
+ cache_flags)
+ if not rc then
+ return false, e
+ end
+ end
+ return true, nil
+end
+
+--- write build driver files
+-- @param info
+-- @param r string: result name
+-- @param destdir string: where to store the scripts
+-- @return bool
+-- @return an error object on failure
+function write_build_driver(info, r, destdir)
+ e2lib.log(4, "writing build driver")
+ local res = info.results[r]
+ local rc, re
+ local e = new_error("generating build driver script failed")
+ local buildrc_file = string.format("%s/%s", destdir,
+ res.build_config.buildrc_file)
+ local buildrc_noinit_file = string.format("%s/%s", destdir,
+ res.build_config.buildrc_noinit_file)
+ local build_driver_file = string.format("%s/%s", destdir,
+ res.build_config.build_driver_file)
+ local bd = ""
+ bd=bd..string.format("source %s/env/builtin\n", res.build_config.Tc)
+ bd=bd..string.format("source %s/env/env\n", res.build_config.Tc)
+ local brc_noinit = bd
+ for x in e2lib.directory(info.root .. "/proj/init") do
+ if not e2lib.is_backup_file(x) then
+ bd=bd..string.format("source %s/init/%s\n", res.build_config.Tc, x)
+ end
+ end
+ bd=bd..string.format("cd %s/build\n", res.build_config.Tc)
+ local brc = bd -- the buildrc file
+ bd=bd..string.format("set\n")
+ bd=bd..string.format("cd %s/build\n", res.build_config.Tc)
+ bd=bd..string.format("source %s/script/build-script\n", res.build_config.Tc)
+ -- write buildrc file (for interactive use)
+ local f, re = io.open(buildrc_file, "w")
+ if not f then
+ return false, e:cat(re)
+ end
+ f:write(brc)
+ f:close()
+ -- write buildrc file (for interactive use, without sourcing init files)
+ local f, re = io.open(buildrc_noinit_file, "w")
+ if not f then
+ return false, e:cat(re)
+ end
+ f:write(brc_noinit)
+ f:close()
+ -- write the build driver
+ local f, re = io.open(build_driver_file, "w")
+ if not f then
+ return false, e:cat(re)
+ end
+ f:write(bd)
+ f:close()
+ return true, nil
+end
+
+--- write the environment script for a result into a file
+-- @param env table: the env table
+-- @param r string: the result name
+-- @param file string: the target filename
+-- @return bool
+-- @return an error object on failure
+function write_environment_script(env, r, file)
+ local e = new_error("writing environment script")
+ local f, msg = io.open(file, "w")
+ if not f then
+ e:append("%s: %s", file, msg)
+ return false, e
+ end
+ for k,v in pairs(env) do
+ if type(v) == "table" and r == k then
+ for k2, v2 in pairs(v) do
+ f:write(string.format("%s=\"%s\"\n", k2, v2))
+ end
+ elseif type(v) == "string" then
+ f:write(string.format("%s=\"%s\"\n", k, v))
+ end
+ end
+ f:close()
+ return true, nil
+end
+
+--- collect all data required to build the project.
+-- skip results that depend on this result
+-- example: toolchain, busybox, sources, iso,
+-- sources being the result collecting the project:
+-- the results sources and iso won't be included, as that would lead to
+-- an impossibility to calculate buildids (infinite recursion)
+-- @param c table: build context
+-- @return bool
+-- @return an error object on failure
+function e2build.collect_project(info, r, return_flags)
+ local res = info.results[r]
+ if not res.collect_project then
+ -- nothing to be done here...
+ return true, nil
+ end
+ e2lib.log(3, "providing project data to this build")
+ local rc, re
+ local e = new_error("providing project data to this build failed")
+ -- project/proj/init/<files>
+ local destdir = string.format("%s/project/proj/init",
+ res.build_config.T)
+ e2lib.mkdir(destdir, "-p")
+ local init_files = e2util.directory(info.root .. "/proj/init")
+ for _,f in ipairs(init_files) do
+ e2lib.log(3, string.format("init file: %s", f))
+ local server = "."
+ local location = string.format("proj/init/%s", f)
+ local cache_flags = {}
+ rc, re = cache.fetch_file(info.cache, server, location,
+ destdir, nil, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ -- write project configuration
+ local file, destdir
+ local lines = ""
+ destdir = string.format("%s/project/proj", res.build_config.T)
+ file = string.format("%s/config", destdir)
+ local f, msg = io.open(file, "w")
+ if not f then
+ return false, e:cat(re)
+ end
+ f:write(string.format("name='%s'\n", info.name))
+ f:write(string.format("release_id='%s'\n", info.release_id))
+ f:write(string.format("default_results='%s'\n",
+ res.collect_project_default_result))
+ f:write(string.format("chroot_arch='%s'\n",
+ info.project.chroot_arch))
+ f:close()
+ -- files from the project
+ local destdir = string.format("%s/project/.e2/bin", res.build_config.T)
+ e2lib.mkdir(destdir, "-p")
+ -- generate build driver file for each result
+ -- project/chroot/<group>/<files>
+ for _,g in pairs(res.collect_project_chroot_groups) do
+ e2lib.log(3, string.format("chroot group: %s", g))
+ local grp = info.chroot.groups_byname[g]
+ local destdir = string.format("%s/project/chroot/%s",
+ res.build_config.T, g)
+ e2lib.mkdir(destdir, "-p")
+ for _,file in pairs(grp.files) do
+ local cache_flags = {}
+ local server = grp.server
+ rc, re = cache.fetch_file(info.cache, server, file,
+ destdir, nil, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ end
+ -- project/licences/<licence>/<files>
+ for _,l in ipairs(res.collect_project_licences) do
+ e2lib.logf(3, "licence: %s", l)
+ local lic = info.licences[l]
+ local destdir = string.format("%s/project/licences/%s",
+ res.build_config.T, l)
+ e2lib.mkdir(destdir, "-p")
+ for _,file in ipairs(lic.files) do
+ local cache_flags = {}
+ local server = lic.server
+ rc, re = cache.fetch_file(info.cache, server, file,
+ destdir, nil, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ end
+ -- project/results/<res>/<files>
+ for _,n in ipairs(res.collect_project_results) do
+ e2lib.log(3, string.format("result: %s", n))
+ local rn = info.results[n]
+ rc, re = e2build.build_config(info, n)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local destdir = string.format("%s/project/res/%s",
+ res.build_config.T, n)
+ e2lib.mkdir(destdir, "-p")
+ -- copy files
+ local files = {
+ string.format("res/%s/build-script", n),
+ }
+ for _,file in pairs(files) do
+ local server = info.root_server_name
+ local cache_flags = {}
+ rc, re = cache.fetch_file(info.cache, server, file,
+ destdir, nil, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ local file, line
+ -- generate environment script
+ file = string.format("%s/env", destdir)
+ rc, re = write_environment_script(info.env, n, file)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- generate builtin environment script
+ local file = string.format("%s/builtin", destdir)
+ rc, re = write_environment_script(
+ rn.build_config.builtin_env, n, file)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- generate build driver
+ rc, re = write_build_driver(info, n, destdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- generate config
+ local config = string.format("%s/config", destdir)
+ local f, msg = io.open(config, "w")
+ if not f then
+ e:cat(new_error("%s: %s", config, msg))
+ return false, e
+ end
+ f:write(string.format(
+ "### generated by e2 for result %s ###\n", n))
+ f:write(string.format(
+ "CHROOT='base %s'\n", table.concat(rn.chroot, " ")))
+ f:write(string.format(
+ "DEPEND='%s'\n", table.concat(rn.depends, " ")))
+ f:write(string.format(
+ "SOURCE='%s'\n", table.concat(rn.sources, " ")))
+ f:close()
+ end
+ for _,s in ipairs(info.results[r].collect_project_sources) do
+ local src = info.sources[s]
+ e2lib.log(3, string.format("source: %s", s))
+ local destdir = string.format("%s/project/src/%s",
+ res.build_config.T, s)
+ e2lib.mkdir(destdir, "-p")
+ local source_set = res.build_mode.source_set()
+ local files, re = scm.toresult(info, src.name, source_set,
+ destdir)
+ if not files then
+ return false, e:cat(re)
+ end
+ end
+ -- write topologically sorted list of result
+ local destdir = string.format("%s/project", res.build_config.T)
+ local tsorted_results = e2tool.dlist_recursive(info,
+ res.collect_project_results)
+ local tsorted_results_string = table.concat(tsorted_results, "\n")
+ local resultlist = string.format("%s/resultlist", destdir)
+ rc, re = e2lib.write_file(resultlist, tsorted_results_string .. "\n")
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- install the global Makefiles
+ local server = "."
+ local destdir = string.format("%s/project", res.build_config.T)
+ local cache_flags = {}
+ local locations = {
+ ".e2/lib/make/Makefile",
+ ".e2/lib/make/linux32.c",
+ ".e2/lib/make/e2-su-2.2.c",
+ ".e2/lib/make/build.sh",
+ ".e2/lib/make/buildall.sh",
+ ".e2/lib/make/detect_tool",
+ }
+ for _,location in ipairs(locations) do
+ rc, re = cache.fetch_file(info.cache, server, location,
+ destdir, nil, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ local script = string.format("%s/buildall.sh", destdir)
+ local rc, re = e2lib.chmod("755", script)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2scm.lua - SCM-specific functionality -*- Lua -*-
+
+
+e2scm = e2lib.module("e2scm")
+
+
+-- General SCM wrapping
+--
+-- SCM-specific functions are implemented as sub-tables of the "e2scm"
+-- namespace and calls to "e2scm.<SCM>.<op>(...)" are simply passed
+-- to the SCM specific entries (after checking whether such an operation
+-- exists for the selected SCM).
+--
+-- e2scm.register(SCMNAME, [TABLE]) -> TABLE
+--
+-- Registers an SCM with the given name and creates the namespace and
+-- returns it (to be filled with operations). If a table is given as
+-- argument, then the argument table is used instead of creating a
+-- fresh one.
+--
+-- e2scm.register_op(OPNAME, DOC)
+--
+-- Registers SCM operation with documentation string.
+--
+
+local scms = {}
+local ops = {}
+local scmmt = {}
+
+function scmmt.__index(t, k)
+ local doc = ops[ k ] or
+ e2lib.bomb("invalid SCM operation `" .. k .. "'")
+ local x = rawget(t, k)
+ return x or e2lib.abort("`" .. t.name .. "' can not " .. ops[ k ])
+end
+
+function e2scm.register(scm, t)
+ t = t or {}
+ t.name = scm
+ scms[ scm ] = t
+ setmetatable(t, scmmt)
+ return t
+end
+
+function e2scm.register_op(op, doc)
+ ops[ op ] = doc
+end
+
+function e2scm.registered(scm)
+ return scms[ scm ]
+end
+
+-- iterate over registrated scm, include "files" when all is true
+function e2scm.iteratescms(all)
+ local k = nil
+ local t = scms
+ local function nextscm(t)
+ k = next(t, k)
+ if k == "files" and not all then k = next(t, k) end
+ return k
+ end
+ return nextscm, t
+end
+
+-- for all scms, add an options flag
+function e2scm.optionsaddflags(all, text)
+ local t = text or "use"
+ for scm in e2scm.iteratescms(all) do
+ e2option.flag(scm, t .. " source of type '" .. scm .. "'")
+ end
+end
+
+-- with options parsed, see which flag has been given with the options
+function e2scm.optionswhichflag(options, default)
+ local f = nil
+ for scm in e2scm.iteratescms(true) do
+ if options[scm] then
+ f = f and e2lib.abort("scm type flags to be used exclusively") or scm
+ end
+ end
+ return f or default or e2lib.abort("no scm type flag given")
+end
+
+local mt = {}
+
+function mt.__index(t, k)
+ local scm = scms[ k ]
+ return scm or e2lib.bomb("no SCM is registered under the name `" .. k .. "'")
+end
+
+setmetatable(e2scm, mt)
+
+scm = {}
+
+local function sourcebyname(info, sourcename)
+ local s = info.sources[sourcename]
+ if not s then
+ return nil, new_error("no source by that name: %s", sourcename)
+ end
+ return s, nil
+end
+
+--- calculate and return the sourceid
+-- @param info
+-- @param sourcename
+-- @param sourceset string: source set
+-- @return string: the sourceid, or nil
+-- @return an error object on failure
+function scm.sourceid(info, sourcename, sourceset)
+ local src = sourcebyname(info, sourcename)
+ local rc, re, e
+ e = new_error("getting sourceid failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].sourceid then
+ e:append("sourceid not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ local sourceid
+ sourceid, re = e2scm[src.type].sourceid(info, sourcename, sourceset)
+ if not sourceid then
+ return nil, re
+ end
+ return sourceid, nil
+end
+
+--- validate a source configuration
+-- @param info
+-- @param sourcename
+-- @return bool
+-- @return an error object on failure
+function scm.validate_source(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("validating source failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].validate_source then
+ e:append("validate_source not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- create a result from a source
+-- @param info
+-- @param sourcename
+-- @param sourceset string: source set
+-- @param directory string: destination path to create the result
+-- @return bool
+-- @return an error object on failure
+function scm.toresult(info, sourcename, sourceset, directory)
+ -- create in directory the following structure:
+ -- ./makefile
+ -- ./source/<files>
+ -- ./licences/<files>.licences -- a list of licences per file
+ -- ...
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].toresult then
+ e:append("toresult not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ local rc, re = e2scm[src.type].toresult(info, sourcename,
+ sourceset, directory)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- prepare a source for building
+-- @param info
+-- @param sourcename
+-- @param sourceset string: source set
+-- @param buildpath string: destination path
+-- @return bool
+-- @return an error object on failure
+function scm.prepare_source(info, sourcename, sourceset, buildpath)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].prepare_source then
+ e:append("prepare_source not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].prepare_source(info, sourcename, sourceset,
+ buildpath)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- fetch a source
+-- @param info
+-- @param sourcename
+-- @return bool
+-- @return an error object on failure
+function scm.fetch_source(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].fetch_source then
+ e:append("fetch_source not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].fetch_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- update a source
+-- @param info
+-- @param sourcename
+-- @return bool
+-- @return an error object on failure
+function scm.update(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].update then
+ e:append("update not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].update(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- sanity check a working copy
+-- @param info
+-- @param sourcename
+-- @return bool
+-- @return an error object on failure
+function scm.check_workingcopy(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].check_workingcopy then
+ e:append(
+ "check_workingcopy not implemented for source type: %s",
+ src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].check_workingcopy(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- check if a working copy is available
+-- @param info
+-- @param sourcename
+-- @return bool
+-- @return an error object on failure
+function scm.working_copy_available(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].working_copy_available then
+ e:append(
+ "working_copy_available not implemented for source "..
+ "type: %s", src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].working_copy_available(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- create a table of lines for display
+-- @param info
+-- @param sourcename
+-- @return a table
+-- @return an error object on failure
+function scm.display(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].display then
+ e:append(
+ "display not implemented for source "..
+ "type: %s", src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].display(info, sourcename)
+ if not rc then
+ return nil, re
+ end
+ return rc, nil
+end
+
+--- check if this scm type supports a working copy
+-- @param info
+-- @param sourcename
+-- @return a table
+-- @return an error object on failure
+function scm.has_working_copy(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re, e
+ e = new_error("calling scm operation failed")
+ if not e2scm[src.type] then
+ return false, e:append("no such source type: %s", src.type)
+ end
+ if not e2scm[src.type].has_working_copy then
+ e:append(
+ "has_working_copy not implemented for source "..
+ "type: %s", src.type)
+ return false, e
+ end
+ rc, re = e2scm[src.type].has_working_copy(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2tool.lua
+--
+-- High-level tools used by the build process and basic build operations.
+
+
+e2tool = e2lib.module("e2tool")
+
+-- store supported config_syntax versions here, the newest one must be listed
+-- first, to maintain correct error messages
+
+local config_syntax_compat = {
+ E2_SYNTAX, -- keep this one, it holds the current syntax.
+ "2_2_0",
+}
+
+-- Information gathering and inquiry
+--
+-- e2tool.collect_project_info([PATH]) -> INFO
+--
+-- Collects project-information from the project directory which is computed
+-- via e2lib.locate_project_root(PATH). Returns a table with the following
+-- entries:
+
+--- info table
+-- @name info
+-- @class table
+-- @field name string: project name
+-- @field root string: project root directory
+-- @field root_server string: url pointing to the project root
+-- @field root_server_name string: name of the root server (".")
+-- @field default_repo_server string: name of the default scm repo server
+-- @field default_files_server string: name of the default files server
+-- @field result_storage (deprecated)
+-- @field default_results table: default result list
+-- @field sources table: sources
+-- @field sources_sorted table: sorted list of sources
+-- @field results table: results
+-- @field results_sorted table: sorted list of results
+-- @field servers table: servers
+-- @field licences table: licences keyed by licence names
+-- @field licences_sorted table: sorted list of licences
+-- @field chroot table: chroot
+-- @field chroot_groups table: chroot groups by name
+-- @field chroot_groups_sorted table: sorted list of chroot groups
+-- @field ghash table: temporary list keeps hashes for chroot groups
+-- (generated by buildid_chrootgroup())
+-- @field build_numbers table: build numbers keyed by result names
+-- @field project_location string: project location relative to the servers
+-- @field release_id string: release identifiert XXX where do we initialize it?
+
+--- table of sources records, keyed by source names
+-- @name sources
+-- @class table
+-- @field name string: name of the package
+-- @field licences table: list of licences
+-- @field type string: type of sources ("files", "git", etc)
+-- @field server string: server name
+-- @field remote string: remote location name
+-- @field working string: working directory name
+-- @field branch string: branch name
+-- @field tag table: table of tag names (strings)
+-- @field file table: table of file records (tables)
+-- @field fhash string: hash value for this source, for use in buildid
+-- calculation
+-- @field flist table: array of files
+-- (deprecated, replaced by file records)
+
+--- file records in the sources table
+-- @name source.file
+-- @class table
+-- @field name string: filename
+-- @field server string: server name
+
+--- table of result records, keyed by result names
+-- @name results
+-- @class table
+-- @field name string: name of the result
+-- @field sources table of strings: array of source names
+-- @field files OBSOLETE table of strings: array of result file names
+-- @field depends table of strings: list of dependencies
+-- @field chroot table of strings: list of chroot groups to use
+-- @field collect_project bool: collect the project structure into this result?
+-- @field collect_project_default_result string: which result shall be
+-- collected, including recursive dependencies?
+-- @field collect_project_results table: sorted list of results to be
+-- collected
+-- @field collect_project_sources table: sorted list of sources to be
+-- collected
+-- @field collect_project_chroot_groups table: sorted list of chroot groups
+-- to be collected
+-- @field collect_project_licences table: sorted list of licences to be
+-- collected
+-- @field export bool: export to the result with collect_project enabled?
+-- @field selected bool: select for build?
+-- @field force_rebuild bool: force rebuild?
+-- @field phash string: partial buildid for this result. (Without taking
+-- dependencies into account.)
+-- @field bhash string: buildid for this result (Taking dependencies into
+-- account.)
+-- @field build_mode table: build mode policy object
+
+--- table of server records, keyed by server name
+-- @name servers
+-- @class table
+-- @field name string: server name
+-- @field path (deprecated)
+-- @field url string: server url
+-- @field cachable bool: cachable flag
+-- @field cache bool: cache enable flag
+
+--- table of chroot configuration
+-- @name chroot
+-- @class table
+-- @field groups table: chroot group table
+
+--- chroot group table
+-- @name chroot group table
+-- @class table
+-- @field name string: group name
+-- @field server string: server name
+-- @field files table: array of file names
+
+--- env - environment table from "proj/env"
+-- @name env
+-- @class table
+
+--
+-- e2tool.load_user_config(INFO, PATH, DEST, INDEX, VAR) --> OK?
+--
+-- from the file at PATH, load a configuration named VAR.
+-- the resulting data is fixated and stored as DEST[INDEX].
+-- fixating is done by the local function tablefixate,
+-- which keeps a list of all array member names in the specific
+-- array member ".fix". furthermore, the type of the configuration
+-- is memorized at ".e2". when storing the configuration back
+-- to permanent memory, only those table entries which are listed
+-- in the ".fix" array will be stored to the configuration file.
+-- storing details are described with e2tool.save_user_config below.
+
+function e2tool.opendebuglogfile(info)
+ e2lib.mkdir(info.root .. "/log", "-p")
+ e2lib.debuglogfile = luafile.open(info.root .. "/log/debug.log", "w")
+end
+
+--- load user configuration file
+-- @param info
+-- @param path string: path to file
+-- @param dest
+-- @param index
+-- @param var
+-- @return bool
+-- @return an error object on failure
+function e2tool.load_user_config(info, path, dest, index, var)
+ local rc, re
+ local e = new_error("loading configuration failed")
+ e2lib.log(3, "loading " .. path)
+ if not e2util.exists(path) then
+ return false, e:append("file does not exist: %s", path)
+ end
+ local function func(table)
+ dest[index] = table
+ end
+ local rc, re = e2lib.dofile_protected(path, { [var] = func, env = info.env, string=string })
+ if not rc then
+ return false, e:cat(re)
+ end
+ if not dest[ index ] then
+ return false, e:append("empty or invalid configuration: %s", path)
+ end
+ return true
+end
+
+--- config item
+-- @class table
+-- @class config_item
+-- @field data table: config data
+-- @field type string: config type
+-- @field filename string: config file name
+
+--- load config file and return a list of config item tables
+-- @param path string: file to load
+-- @param type list of strings: allowed config types
+-- @return list of config items
+-- @return an error object on failure
+function e2tool.load_user_config2(info, path, types)
+ local e = new_error("loading configuration file failed")
+ local rc, re
+ local list = {}
+
+ -- the list of config types
+ local f = {}
+ f.e2source = function(data)
+ local t = {}
+ t.data = data
+ t.type = "sources"
+ t.filename = path
+ table.insert(list, t)
+ end
+ f.e2result = function(data)
+ local t = {}
+ t.data = data
+ t.type = "result"
+ t.filename = path
+ table.insert(list, t)
+ end
+ f.e2project = function(data)
+ local t = {}
+ t.data = data
+ t.type = "project"
+ t.filename = path
+ table.insert(list, t)
+ end
+ f.e2chroot = function(data)
+ local t = {}
+ t.data = data
+ t.type = "chroot"
+ t.filename = path
+ table.insert(list, t)
+ end
+ f.e2env = function(data)
+ local t = {}
+ t.data = data
+ t.type = "env"
+ t.filename = path
+ table.insert(list, t)
+ end
+
+ local g = {} -- compose the environment for the config file
+ g.env = info.env -- env
+ g.string = string -- string
+ for _,type in ipairs(types) do
+ g[type] = f[type] -- and some config functions
+ end
+
+ rc, re = e2lib.dofile2(path, g)
+ if not rc then
+ return nil, e:cat(re)
+ end
+ return list, nil
+end
+
+function e2tool.collect_project_info(path)
+ local rc, re
+ local e = new_error("reading project configuration")
+
+ local info = {}
+ info.root, re = e2lib.locate_project_root(path)
+ if not info.root then
+ return false, e:append("you are not located in a project directory")
+ end
+ rc, re = e2tool.lcd(info, ".")
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ -- check for configuration compatibility
+ info.config_syntax_compat = config_syntax_compat
+ info.config_syntax_file = ".e2/syntax"
+ rc, re = e2tool.check_config_syntax_compat(info)
+ if not rc then
+ local s = [[
+Your configuration syntax is incompatible with this tool version.
+Please read the configuration Changelog, update your project configuration
+and finally insert the new configuration syntax version into %s
+
+The newest configuration syntax supported by the tools is %s.
+]]
+ e2lib.logf(2, s, info.config_syntax_file, info.config_syntax_compat[1])
+ e2lib.finish(1)
+ end
+
+ -- try to get project specific config file paht
+ local config_file_config = string.format("%s/.e2/e2config", info.root)
+ local config_file = e2lib.read_line(config_file_config)
+ -- don't care if this succeeds, the parameter is optional.
+
+ local rc, re = e2lib.read_global_config(config_file)
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ info.local_template_path = string.format("%s/.e2/lib/e2/templates",
+ info.root)
+
+ e2lib.init2() -- configuration must be available
+
+ e2tool.opendebuglogfile(info)
+
+ --XXX create some policy module where the following policy settings
+ --XXX and functions reside (server names, paths, etc.)
+
+ -- the '.' server as url
+ info.root_server = "file://" .. info.root
+ info.root_server_name = "."
+
+ -- the proj_storage server is equivalent to
+ -- info.default_repo_server:info.project-locaton
+ info.proj_storage_server_name = "proj-storage"
+
+ -- need to configure the results server in the configuration, named 'results'
+ info.result_server_name = "results"
+
+ info.default_repo_server = "projects"
+ info.default_files_server = "upstream"
+
+ -- build modes
+ info.build_modes = { "tag", "branch" }
+
+ -- the build mode policy used
+ info.build_mode = nil
+
+ -- prefix the chroot call with this tool (switch to 32bit on amd64)
+ -- XXX not in buildid, as it is filesystem location dependent...
+ info.chroot_call_prefix = {}
+ info.chroot_call_prefix["x86_32"] = string.format("%s/.e2/bin/e2-linux32",
+ info.root)
+ -- either we are on x86_64 or we are on x86_32 and refuse to work anyway
+ -- if x86_64 mode is requested.
+ info.chroot_call_prefix["x86_64"] = ""
+
+ -- build number state file
+ info.buildnumber_file = string.format("%s/.e2/build-numbers", info.root)
+
+ -- build number table
+ info.build_numbers = {}
+
+ -- checksum file suffixes, ordered by priority
+ info.default_checksum_file_suffix = ".sha1"
+
+ info.sources = {}
+
+ -- read project environment file
+ local p2 = info.root .. "/proj/env"
+ local function invalid_access(table, k, v)
+ e2lib.abort("attempt to add an entry `", k, "' with value '",
+ v, "' to the project environment table, the environment",
+ " may not be extended once it is loaded.")
+ end
+ info.env = {}
+ if e2util.exists(p2) then
+ e2lib.log(3, "loading " .. p2)
+ local function check(k, v)
+ local t = type(v)
+ if t ~= "string" and t ~= "number" then
+ e2lib.abort("invalid environment value for `", k,
+ "' in proj/env, value must be a string or number")
+ end
+ end
+ local lua_should_have_localrec
+ local env = function(tab)
+ if type(tab) == "string" then
+ local path2 = info.root .. "/" .. tab
+ e2lib.log(3, "loading " .. path2)
+ e2lib.dofile_protected(path2, { env=lua_should_have_localrec })
+ elseif type(tab) == "table" then
+ for k, v in pairs(tab) do -- for each result...
+ if type(k) ~= "string" then
+ e2lib.abort("invalid environment key `", k,
+ "' in proj/env, key must be a string")
+ end
+ local t = type(v)
+ if t == "table" then
+ table.foreach(v, check)
+ else
+ check(k, v)
+ end
+ end
+ -- *** we go again through the table - can this be fused?
+ for k, v in pairs(tab) do
+ local t = info.env[ k ]
+ if not t then t = {} end
+ if type(v) == "table" then
+ for k2, v2 in pairs(v) do
+ t[ k2 ] = v2
+ end
+ else
+ t = v
+ end
+ info.env[ k ] = t
+-- print("info.env[", k, "]:", t)
+-- if type(t) == "table" then
+-- table.foreach(t, print)
+-- end
+-- print("info.env:")
+-- table.foreach(info.env, print)
+ end
+ else
+ e2lib.abort("invalid argument to `env' - should be string or table")
+ end
+ end
+ lua_should_have_localrec = env
+ local rc, re = e2lib.dofile_protected(p2, { env = env, e2env = info.env })
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- do not set this metatable here, as we want to allow changing
+ -- the environment in a hook...
+ --setmetatable(info.env, { __newindex = invalid_access })
+ end
+
+ -- read project configuration
+ local rc, re = e2tool.load_user_config(info, info.root .. "/proj/config",
+ info, "project", "e2project")
+ if not rc then
+ return false, e:cat(re)
+ end
+ info.project[".fix"] = nil
+ local e = new_error("in project configuration:")
+ if not info.project.release_id then
+ e:append("key is not set: release_id")
+ end
+ if not info.project.name then
+ e:append("key is not set: name")
+ end
+ if type(info.project.default_results) ~= "table" then
+ e2lib.warnf("WDEFAULT", "in project configuration:")
+ e2lib.warnf("WDEFAULT",
+ " default_results ist not a table. Defaulting to empty list.")
+ end
+ if not info.project.chroot_arch then
+ e2lib.warnf("WDEFAULT", "in project configuration:")
+ e2lib.warnf("WDEFAULT", " chroot_arch defaults to x86_32")
+ info.project.chroot_arch = "x86_32"
+ end
+ if not info.chroot_call_prefix[info.project.chroot_arch] then
+ e:append("chroot_arch is set to an invalid value")
+ end
+ if info.project.chroot_arch == "x86_64" and
+ e2lib.host_system_arch ~= "x86_64" then
+ e:append("running on x86_32: switching to x86_64 mode is impossible.")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ info.release_id = info.project.release_id
+ info.name = info.project.name
+ info.default_results = info.project.default_results
+
+ local function add_builtin_servers(info)
+ local function add_server(info, name, url, cachable)
+ e2lib.warnf("WDEFAULT", "setting up builtin server:")
+ e2lib.warnf("WDEFAULT", " %s [%s]", name, url)
+ local s = {}
+ s.name = name
+ s.url = url
+ s.cachable = cachable
+ if info.servers[name] then
+ return false, new_error(
+ "cannot setup builtin server %s: server exists", name)
+ end
+ info.servers[name] = s
+ return true, nil
+ end
+ local rc, re
+ rc, re = add_server(info, info.root_server_name, info.root_server, false)
+ if not rc then
+ return false, re
+ end
+ info.servers[info.root_server_name].writeback = true
+ if not info.servers[info.default_repo_server] then
+ e2lib.warnf(WPOLICY, "server %s is unconfigured.",
+ info.default_repo_server)
+ e2lib.warnf(WPOLICY, "Cannot setup server %s",
+ info.proj_storage_server_name)
+ -- do not treat that as an error, unless the server is used.
+ return true, nil
+ end
+ -- create the new url
+ local proj_storage_server = string.format("%s/%s",
+ info.servers[info.default_repo_server].url, info.project_location)
+ rc, re = add_server(info, info.proj_storage_server_name,
+ proj_storage_server, true)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+ end
+
+ -- chroot config
+ info.chroot_config_file = "proj/chroot"
+ rc, re = e2tool.read_chroot_config(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ -- licences
+ rc, re = e2tool.load_user_config(info, info.root .. "/proj/licences",
+ info, "licences", "e2licence")
+ if not rc then
+ return false, e:cat(re)
+ end
+ info.licences[".fix"] = nil
+ -- privide sorted list of licences
+ info.licences_sorted = {}
+ for l,lic in pairs(info.licences) do
+ table.insert(info.licences_sorted, l)
+ end
+ table.sort(info.licences_sorted)
+
+ rc, re = e2tool.load_source_config(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ rc, re = e2tool.load_result_config(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ -- servers
+ info.server_default_config = { -- default values
+ cachable = true,
+ }
+
+ -- read .e2/proj-location
+ info.project_location_config = string.format("%s/.e2/project-location",
+ info.root)
+ local line, re = e2lib.read_line(info.project_location_config)
+ if not line then
+ return false, e:cat(re)
+ end
+ local s, e, l = string.find(line, "^%s*(%S+)%s*$")
+ if not l then
+ return false, e:append("%s: can't parse project location",
+ info.project_location_config)
+ end
+ info.project_location = l
+ e2lib.log(4, string.format("project location is %s", info.project_location))
+ local config = e2lib.get_global_config()
+ info.servers = config.servers
+ if not info.servers then
+ return false, e:append("no servers configured in global configuration")
+ end
+ local rc, re = add_builtin_servers(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+ info.servers[".fix"] = nil
+ for name, server in pairs(info.servers) do
+ server.name = name
+ -- apply default values
+ for k, v in pairs(info.server_default_config) do
+ if server[k] == nil then
+ server[k] = info.server_default_config[k]
+ end
+ end
+ -- print for debugging purposes
+ for k, v in pairs(server) do
+ v = tostring(v)
+ e2lib.log(4, string.format("%-20s: %-10s %s", name, k, v))
+ end
+ end
+
+ -- warn if deprecated config files still exist
+ local deprecated_files = {
+ "proj/servers",
+ "proj/result-storage",
+ "proj/default-results",
+ "proj/name",
+ "proj/release-id",
+ }
+ for _,f in ipairs(deprecated_files) do
+ local path = string.format("%s/%s", info.root, f)
+ if e2util.exists(path) then
+ e2lib.warnf("WDEPRECATED", "File exists but is no longer used: `%s'", f)
+ end
+ end
+
+ local cache_url
+ if config.cache and config.cache.path then
+ -- replace %u by the username, %l by the project location
+ local replace = { u=e2lib.username, l=info.project_location }
+ local cache_path = e2lib.format_replace(config.cache.path, replace)
+ cache_url = string.format("file://%s", cache_path)
+ else
+ cache_url = string.format("%s/cache", info.root_server)
+ e2lib.warnf("WPOLICY", "cache defaulting to %s", cache_url)
+ end
+ info.cache, re = e2lib.setup_cache("local cache", cache_url, info.servers)
+ if not info.cache then
+ return false, e:cat(re)
+ end
+
+ --e2tool.add_source_results(info)
+
+ -- provide a sorted list of results
+ info.results_sorted = {}
+ for r,res in pairs(info.results) do
+ table.insert(info.results_sorted, r)
+ end
+ table.sort(info.results_sorted)
+
+ -- provided sorted list of sources
+ info.sources_sorted = {}
+ for s,src in pairs(info.sources) do
+ table.insert(info.sources_sorted, s)
+ end
+ table.sort(info.sources_sorted)
+
+ -- provide sorted list of servers
+ info.servers_sorted = {}
+ for s, srv in pairs(info.servers) do
+ table.insert(info.servers_sorted, s)
+ end
+ table.sort(info.servers_sorted)
+
+ rc, re = policy.init(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return info, nil
+end
+
+--
+-- e2tool.check_project_info(INFO, ALL, [ACCESS, [VERBOSE]]) -> BOOLEAN
+--
+-- Checks project information for consistancy
+-- When ALL is false, check only those results/sources reachable from
+-- the dependency list
+-- When ACCESS is true, checks also server locations
+-- When VERBOSE is true, sends error messages to stderr
+
+function e2tool.check_project_info(info, all, access, verbose)
+ local rc, re
+ local e = new_error("error in project configuration")
+ rc, re = e2tool.check_chroot_config(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2tool.check_sources(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2tool.check_results(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = e2tool.check_licences(info)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc = e2tool.dsort(info)
+ if not rc then
+ return false, e:cat("cyclic dependencies")
+ end
+ return true, nil
+end
+
+
+-- Save user configuration file
+--
+-- e2tool.save_user_config(PATH, CFG)
+--
+-- Save a partial project configuration (source or result)
+-- into a file named PATH
+-- CFG is one of info.sources[s], info.results[r], info.chroot,
+-- info.licences
+--
+-- e2tool.config_create(CONFIGTYPE) -> CGF
+--
+-- Create and return an empty configuration.
+-- CONFIGTYPE is one of "e2source", "e2result", "e2chroot",
+-- "e2licence".
+--
+-- e2tool.config_insert(CFG, KEY, VALUE)
+--
+-- Add a new field to a source/result configuration entry.
+
+function e2tool.save_user_config(path, entry)
+ local function save_field(file, indent, key, value, ender)
+ file:write(ender .. "\n" .. string.rep(" ", indent))
+ if type(key) ~= "number" then file:write(key .. " = ") end
+ if type(value) == "string" then
+ file:write("\"" .. value .. "\"")
+ elseif type(value) == "number" or type(value) == "boolean" then
+ file:write(tostring(value))
+ elseif type(value) == "table" then
+ local e = "{"
+ for k, v in pairs(value) do
+ save_field(file, indent+1, k, v, e)
+ e = ","
+ end
+ if e == "," then
+ file:write("\n" .. string.rep(" ", indent) .. "}")
+ else
+ file:write("{}")
+ end
+ else
+ e2lib.bomb("unexpected data type in info field entry: "
+ .. type(value) .. " at " .. key)
+ end
+ end
+ local x = entry[".fix"]
+ if not x then e2lib.abort("fixature missing: " .. path) end
+ local f, msg = io.open(path, "w")
+ if not f then e2lib.abort("cannot write config " .. path .. ":" .. msg) end
+ f:write("-- config -*- Lua -*-\n\n")
+ f:write(x[".e2"] .. " ")
+ local e = "{"
+ for _, k in ipairs(x) do
+ save_field(f, 1, k, entry[k], e)
+ e = ","
+ end
+ f:write("\n}\n")
+ f:close()
+end
+
+function e2tool.config_create(configtype)
+ if configtype ~= "e2source" and
+ configtype ~= "e2result" and
+ configtype ~= "e2chroot" and
+ configtype ~= "e2licence" then
+ e2lib.abort("unknown configuration type: " .. configtype)
+ end
+ local f = {}
+ f[".e2"] = configtype
+ local c = {}
+ c[".fix"] = f
+ return c
+end
+
+function e2tool.config_insert(entry, key, value)
+ local k = key or (#entry + 1)
+ entry[k] = value
+ table.insert(entry[".fix"], k)
+end
+
+-- Debugging tools
+--
+-- e2tool.show_project_info(INFO)
+--
+-- Performs a simple dump of the project information in INFO.
+
+function e2tool.show_project_info(info)
+ print("name:", info.name)
+ print("root:", info.root)
+ print("results: ", info.servers[result_server_name].url)
+ print("\nsources:")
+ for i, src in pairs(info.sources) do
+ print()
+ for k, v in pairs(src) do
+ if k == "file" then
+ for f, t in pairs(v) do
+ print("", k, t.server, t.name)
+ end
+ else
+ print("", k, v)
+ end
+ end
+ end
+ print("\nresults:")
+ for i, res in pairs(info.results) do
+ print()
+ for k, v in pairs(res) do
+ print("", k, v)
+ if type(v) == "table" and k ~= ".fix" then
+ for u, b in pairs(v) do
+ print("", "", u, b)
+ end
+ end
+ end
+ end
+ print("\nservers:")
+ for i, srv in pairs(info.servers) do
+ print("", srv.name, srv.url)
+ end
+ print("\ndefault results:")
+ for k, v in pairs(info.default_results) do
+ print("", v)
+ end
+end
+
+
+-- Dependency management
+--
+-- e2tool.dsort(INFO) -> ARRAY
+--
+-- Returns an array with the names of all results of the project specified
+-- by INFO, topologically sorted according to the projects dependency
+-- information.
+--
+-- e2tool.dlist(INFO, RESULT) -> ARRAY
+--
+-- Returns a sorted array with all dependencies for the given RESULT in the
+-- project specified by INFO, the RESULT itself excluded.
+--
+-- e2tool.dlist_recursive(INFO, RESULT) -> ARRAY
+--
+-- Similar to e2tool.dlist(), but also includes indirect dependencies.
+-- If RESULT is a table, calculate dependencies for all elements, inclusive,
+-- otherwise calculate dependencies for RESULT, exclusive.
+
+function e2tool.dlist(info, res)
+ local t = info.results[res] and info.results[res].depends or {}
+ table.sort(t)
+ return t
+end
+
+function e2tool.dlist_recursive(info, result)
+ local had = {}
+ local path = {}
+ local col = {}
+ local t = {}
+ local function visit(res)
+ if had[res] then
+ e2lib.warn("WOTHER", "cyclic dependency: " .. table.concat(path, " "))
+ t = nil
+ elseif t and not col[res] then
+ table.insert(path, res)
+ had[res] = true
+ col[res] = true
+ for _, d in ipairs(e2tool.dlist(info, res)) do visit(d) end
+ if t then table.insert(t, res) end
+ had[res] = nil
+ path[#path] = nil
+ end
+ end
+ for _, r in ipairs(
+ type(result) == "table" and result or e2tool.dlist(info, result)) do
+ visit(r)
+ end
+ return t
+end
+
+function e2tool.dsort(info)
+ return e2tool.dlist_recursive(info, info.default_results)
+end
+
+
+-- Server handling
+--
+-- e2tool.lookup_server(INFO, SRV) -> ABSOLUTE_PATH, BOOLEAN
+--
+-- For a given project internal server name, returns its absolute path
+-- and true if the server is the project directory itself, false otherwise
+--
+-- e2tool.find_server(INFO, PATH) -> SERVERNAME, RELATIVE_PATH
+--
+-- Return servername and relative path for given PATH or nil.
+
+function e2tool.lookup_server(info, srv)
+ e2lib.log(1, "e2tool.lookup_server() is deprecated")
+ local path, e = cache.file_path(info.cache, srv, "", {})
+ if not path then
+ e2lib.abort(e)
+ return nil
+ end
+ return path
+end
+
+--- e2tool.valid_server
+-- @param info info data
+-- @param srv string: server name
+-- @return true if the server is valid, false otherwise
+function e2tool.valid_server(info, srv)
+ if info.servers[srv] then
+ return true
+ end
+ return false
+end
+
+function e2tool.find_server(info, path)
+ e2lib.log(1, "e2tool.find_server() is deprecated and may not work")
+ -- XXX find all callers and replace with something else
+ if #path > 0 then
+ local statf
+ local s = e2util.realpath(info.root)
+ if s then
+ statf = e2util.stat(s)
+ if statf and statf.type == "directory" and "/" ~= string.sub(s, -1) then
+ s = s .. "/"
+ end
+ end
+ local p = e2util.realpath(path)
+ if p then
+ statf = e2util.stat(p)
+ if statf and statf.type == "directory" and "/" ~= string.sub(p, -1) then
+ p = p .. "/"
+ end
+ if #p >= #s + 2 and s == string.sub(p, 1, #s) then
+ return ".", string.sub(p, #s + 1)
+ end
+ for k, v in pairs(info.servers) do
+ if string.match(v.path, ":") then
+ return path, ""
+ end
+ s = e2util.realpath(v.path)
+ if #p >= #s + 2 and s == string.sub(p, 1, #s) then
+ return k, string.sub(p, #s + 2)
+ end
+ end
+ end
+ end
+ return nil
+end
+
+
+-- Retrieval of files information
+--
+-- e2tool.expanded_files_list(INFO, SOURCE)
+--
+-- For the given source of type file, calculate the absolute pathname of
+-- the file, expand directories to the files it contains. The resulting
+-- list is cached to INFO.sources.*.flist and returned, with the
+-- absolute filename as key and the value telling whether it is local.
+
+function e2tool.read_hash_file(info, server, location)
+ local e = new_error("error reading hash file")
+ local cs = nil
+ local cache_flags = { cache = true }
+ local rc, re = cache.cache_file(info.cache, server, location, cache_flags)
+ if not rc then
+ return nil, e:cat(re)
+ end
+ local path = cache.file_path(info.cache, server, location, cache_flags)
+ if path then
+ cs = e2lib.read_line(path)
+ if cs then
+ return cs, nil
+ end
+ end
+ return nil, e:append("can't open checksum file")
+end
+
+--- hash a file
+-- @param path string: path to a file
+-- @return string the hash value, nil on error
+-- @return nil, an error string on error
+function e2tool.hash_path(path)
+ e2lib.log(4, string.format("hashing %s", path))
+ local f = io.popen(string.format("sha1sum %s", path), "r")
+ if not f then
+ return false, "can't calculation checksum"
+ end
+ local l = f:read("*l")
+ f:close()
+ if not l then
+ return nil, "can't calculation checksum"
+ end
+ local s, e, cs, b = string.find(l, "^(%S+)%s+(%S+)$")
+ if not cs then
+ return nil, "can't calculation checksum"
+ end
+ return cs
+end
+
+--- hash a file addressed by server name and location
+-- @param info info structure
+-- @param server the server name
+-- @param location file location relative to the server
+-- @return string the hash value, nil on error
+-- @return nil, an error string on error
+function e2tool.hash_file(info, server, location)
+ local e = new_error("error hashing file")
+ local cache_flags = { cache = true }
+ local rc, re = cache.cache_file(info.cache, server, location, cache_flags)
+ if not rc then
+ return nil, e:cat(re)
+ end
+ local path, re = cache.file_path(info.cache, server, location, cache_flags)
+ if not path then
+ return nil, e:cat(re)
+ end
+ return e2tool.hash_path(path)
+end
+
+function e2tool.expanded_files_list(info, source)
+ e2lib.log(4, "expanded_files_list: " .. source)
+
+ local function add_to_files_list(info, flist, file)
+ e2lib.log(4, "add_to_files_list: " .. file.name)
+ local rserv, islocal = e2tool.lookup_server(info, file.server)
+ if not rserv then e2lib.abort("cannot resolve server: " .. file.server) end
+ local absol = rserv .. "/" .. file.name
+ local dir, base, ftyp = e2lib.splitpath(absol)
+ if not dir then
+ e2lib.abort(base .. ": " .. file.name)
+ end
+ if ftyp ~= "regular" then
+ e2lib.abort("is not a regular file: " .. file.name)
+ end
+ local t = {}
+ for k, v in pairs(file) do t[k] = v end
+ t.absol = absol
+ t.islocal = islocal
+ t.base = base
+ table.insert(flist, t)
+ end
+ local s = info.sources[source]
+ if s.flist then return s.flist end
+ if not s.file then return nil end
+ s.flist = {}
+ for _, t in ipairs(s.file) do
+ add_to_files_list(info, s.flist, t)
+ end
+ for _,f in pairs(s.flist) do
+ e2lib.log(4, "expanded_files_list: s.flist[x].absol: " .. f.absol)
+ end
+ return s.flist
+end
+
+function e2tool.projid(info)
+ if info.projid then
+ return info.projid
+ end
+ -- catch proj/init/*
+ local hc = hash.hash_start()
+ for f in e2lib.directory(info.root .. "/proj/init") do
+ if not e2lib.is_backup_file(f) then
+ local location = string.format("proj/init/%s",
+ e2lib.basename(f))
+ local hash, e = e2tool.hash_file(info,
+ info.root_server_name, location)
+ if not hash then
+ e2lib.abort(e)
+ end
+ hc:hash_line(location) -- the filename
+ hc:hash_line(hash) -- the file content
+ end
+ end
+ for f in e2lib.directory(info.root .. "/proj/hooks", false, true) do
+ if not e2lib.is_backup_file(f) then
+ local location = string.format("proj/hooks/%s",
+ e2lib.basename(f))
+ local hash, e = e2tool.hash_file(info,
+ info.root_server_name, location)
+ if not hash then
+ e2lib.abort(e)
+ end
+ hc:hash_line(location) -- the file location
+ hc:hash_line(hash) -- the file content
+ end
+ end
+ hc:hash_line(info.release_id)
+ hc:hash_line(info.project.chroot_arch)
+ hc:hash_line(E2_VERSION)
+ info.projid = hc:hash_finish()
+ return info.projid
+end
+
+-- Check if e2 is in a fixed tag
+--
+-- e2tool.e2_has_fixed_tag(info)
+--
+-- return true if e2 is at fixed tag, and false if not.
+
+function e2tool.e2_has_fixed_tag(info)
+ local v = e2lib.parse_e2versionfile(info.root .. "/.e2/e2version")
+ e2lib.log(2, "Checking for fixed e2 tag.")
+ if v.tag == "^" then
+ e2lib.log(1, "Fatal: e2 is not at a fixed tag.")
+ return false
+ end
+ return true
+end
+
+-- Check if a tag exists on the e2 tool repository
+--
+-- e2tool.e2_tag_exists(tag)
+--
+-- return true if the tag exists and false if not.
+
+function e2tool.e2_tag_exists(tag)
+ local rc = e2scm["git"].tag_available(tag, nil)
+ if rc then
+ e2lib.log(1, "Fatal: Tag exists in the local repository. FIXME")
+ return true
+ end
+ return false
+end
+
+-- Check if there are sources which are "on pseudo tags"
+--
+-- e2tool.has_pseudotags(info)
+--
+-- Return true if there is at least one source on a pseudo
+-- tag.
+
+function e2tool.has_pseudotags(info)
+ local rc=false
+ local l={}
+ e2lib.log(2, "Checking for pseudo tagged sources.")
+ for _,s in pairs(info.sources) do
+ if s.tag and s.tag == "^" then
+ e2lib.log(1, "Fatal: source " .. s.name .. " has pseudo tag.")
+ rc=true
+ table.insert(l, s.name)
+ end
+ end
+ return rc, l
+end
+
+-- Check if tags are available for all sources
+--
+-- e2tool.tag_available(info, check_local, check_remote)
+--
+-- Return true if the tags are available, false if not.
+-- Choose local and remote checking by setting check_local and
+-- check_remote.
+--
+-- TODO: works with the null project. Use and/or write scm specific
+-- code to make it usable for projects that use non-git scms.
+
+function e2tool.tag_available(info, check_local, check_remote)
+ local missing_local = {}
+ local missing_remote = {}
+ local rc = true
+ --*** this code is basically broken and git-version specific
+ e2lib.log(2, "Checking for tag availability.")
+ for _,s in pairs(info.sources) do
+ if s.tag and check_local then
+ local cmd = "GIT_DIR=in/" .. s.name
+ .. "/.git git rev-list --max-count=1 refs/tags/"
+ .. s.tag .. " --"
+ rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ e2lib.log(1, "Fatal: source " .. s.name
+ .. ": local tag not available: " .. s.tag)
+ rc = false
+ end
+ end
+ if s.tag and check_remote then
+ local cmd = "GIT_DIR=" .. e2tool.lookup_server(info, s.server) .. "/"
+ .. s.remote
+ .. " git rev-list --max-count=1 refs/tags/"
+ .. s.tag .. " --"
+ rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ e2lib.log(1, "Fatal: " .. s.name .. ": remote tag not available: "
+ .. s.tag)
+ rc = false
+ end
+ end
+ end
+end
+
+-- Do all checks required before tagging a project
+--
+-- e2tool.pre_tag_check(info, check_local, check_remote)
+--
+-- Return true if all checks succeed and false if not.
+-- For offline usage local and remote checking can be turned on
+-- as needed.
+
+function e2tool.pre_tag_check(info, tag, check_local, check_remote)
+ -- do all checks first
+ local e2_has_fixed_tag_flag, has_pseudotags_flag, has_pseudotags_list
+ local tag_unavailable_flag, e2_tag_exists_flag
+ e2_has_fixed_tag_flag = e2tool.e2_has_fixed_tag(info)
+ has_pseudotags_flag, has_pseudotags_list = e2tool.has_pseudotags(info)
+ tag_unavailable_flag = e2tool.tag_available(info, check_local, check_remote)
+ if tag then
+ e2_tag_exists_flag = e2tool.e2_tag_exists(tag)
+ else
+ e2_tag_exists_flag = false
+ end
+
+ -- return false if any fatal errors occured
+ if not e2_has_fixed_tag_flag or
+ has_pseudotags_flag or
+ tag_unavailable_flag or
+ e2_tag_exists_flag then
+ return false
+ end
+ return true
+end
+
+--- calculate sourceids for all sources
+-- @param info
+-- @param sourceset
+-- @return bool
+-- @return an error object on failure
+function e2tool.calc_sourceids(info, sourceset)
+ local e = new_error("calculating sourceids failed")
+ for _,src in pairs(info.sources) do
+ local sourceid, re = scm.sourceid(info, src.name, sourceset)
+ if not sourceid then
+ e:cat(re)
+ end
+ end
+ if e.getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- calculate licence id
+-- @param info
+-- @param licence
+-- @return string
+-- @return an error object on failure
+function e2tool.licenceid(info, licence)
+ local rc, re
+ local e = new_error("calculating licence id failed for licence: %s",
+ licence)
+ local lic = info.licences[licence]
+ if lic.licenceid then
+ return lic.licenceid
+ end
+ local hc = hash.hash_start()
+ hc:hash_line(licence) -- licence name
+ hc:hash_line(lic.server) -- server name
+ for _,file in ipairs(lic.files) do
+ local h, re = e2tool.hash_file(info, lic.server, file)
+ if not h then
+ return nil, e:cat(re)
+ end
+ hc:hash_line(file) -- licence file name
+ hc:hash_line(h) -- licence file content
+ end
+ lic.licenceid, re = hc:hash_finish()
+ if not lic.licenceid then
+ return nil, e:cat(re)
+ end
+ return lic.licenceid
+end
+
+--- calculate licenceids for all licences
+-- @param info
+-- @return bool
+-- @return an error object on failure
+function e2tool.calc_licenceids(info)
+ local e = new_error("calculating licenceids failed")
+ for l,_ in pairs(info.licences) do
+ local licenceid, re = e2tool.licenceid(info, l)
+ if not licenceid then
+ e:cat(re)
+ end
+ end
+ if e.getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- return the first eight digits of buildid hash
+-- @param buildid string: hash value
+-- @return string: a short representation of the hash value
+function e2tool.bid_display(buildid)
+ return string.format("%s...", string.sub(buildid, 1, 8))
+end
+
+--- get the buildid for a result, calculating it if required
+-- XXX this function always succeeds or aborts
+-- @param info
+-- @param resultname
+-- @param mode
+-- @return the buildid
+function e2tool.buildid(info, resultname)
+ e2lib.log(4, string.format("get buildid for %s", resultname))
+ local r = info.results[resultname]
+ local id, e = e2tool.pbuildid(info, resultname)
+ if not id then
+ e2lib.abort(e)
+ end
+ local hc = hash.hash_start()
+ hc:hash_line(r.buildno)
+ hc:hash_line(r.pbuildid)
+ r.buildid = hc:hash_finish()
+ return r.build_mode.buildid(r.buildid)
+end
+
+--- get the pbuildid for a result, calculating it if required
+-- XXX this function always succeeds or aborts
+-- @param info
+-- @param resultname
+-- @return the buildid
+function e2tool.pbuildid(info, resultname)
+ e2lib.log(4, string.format("get pbuildid for %s", resultname))
+ local e = new_error("calculating result id failed")
+ local r = info.results[resultname]
+ if r.pbuildid then
+ return r.build_mode.buildid(r.pbuildid)
+ end
+ local hc = hash.hash_start()
+ for _,s in ipairs(r.sources) do
+ local src = info.sources[s]
+ local source_set = r.build_mode.source_set()
+ local sourceid, re =
+ scm.sourceid(info, s, source_set)
+ if not sourceid then
+ return nil, e:cat(re)
+ end
+ hash.hash_line(hc, s) -- source name
+ hash.hash_line(hc, sourceid) -- sourceid
+ end
+ for _,d in ipairs(r.depends) do
+ hash.hash_line(hc, d) -- dependency name
+ end
+ for _,c in ipairs(r.collect_project_results) do
+ hash.hash_line(hc, c) -- name
+ end
+ for _,s in ipairs(r.collect_project_sources) do
+ hash.hash_line(hc, s) -- name
+ end
+ for _,g in ipairs(r.collect_project_chroot_groups) do
+ hash.hash_line(hc, g) -- name
+ end
+ for _,l in ipairs(r.collect_project_licences) do
+ hash.hash_line(hc, l) -- name
+ -- We collect all licences. So we cannot be sure to catch
+ -- them via results/sources. Include them explicitly here.
+ local lid, re = e2tool.licenceid(info, l)
+ if not lid then
+ return nil, e:cat(re)
+ end
+ hash.hash_line(hc, lid) -- licence id
+ end
+ local groupid = e2tool.chrootgroupid(info, "base")
+ hc:hash_line(groupid)
+ if r.chroot then
+ for _,g in ipairs(r.chroot) do
+ local groupid = e2tool.chrootgroupid(info, g)
+ hash.hash_line(hc, g)
+ hash.hash_line(hc, groupid)
+ end
+ end
+ r.envid = e2tool.envid(info, resultname)
+ hc:hash_line(r.envid)
+ if not r.pseudo_result then
+ local location = string.format("res/%s/build-script",
+ resultname)
+ local hash, re = e2tool.hash_file(info,info.root_server_name,
+ location)
+ if not hash then
+ return nil, e:cat(re)
+ end
+ hc:hash_line(hash) -- build script hash
+ end
+ e2lib.log(4, string.format("hash data for resultid %s\n%s",
+ resultname, hc.data))
+ r.resultid = hash.hash_finish(hc) -- result id (without deps)
+
+ hc = hash.hash_start()
+ local projid = e2tool.projid(info)
+ hc:hash_line(projid) -- project id
+ hash.hash_line(hc, r.resultid) -- result id
+ for _,d in ipairs(r.depends) do
+ local id = e2tool.pbuildid(info, d)
+ hash.hash_line(hc, id) -- buildid of dependency
+ end
+ for _,c in ipairs(r.collect_project_results) do
+ local res = info.results[c]
+ -- pbuildids of collected results
+ hash.hash_line(hc, e2tool.pbuildid(info, c))
+ end
+ e2lib.log(4, string.format("hash data for resultid %s\n%s",
+ resultname, hc.data))
+ r.pbuildid = hash.hash_finish(hc) -- buildid (with deps)
+ return r.build_mode.buildid(r.pbuildid)
+end
+
+--- calculate the buildids for all results
+-- @param info
+-- @return nothing
+function e2tool.calc_buildids(info)
+ e2lib.logf(3, "calculating buildids")
+ for _,r in ipairs(info.results) do
+ local bid, pbid
+ bid = e2tool.buildid(info, r)
+ pbid = e2tool.pbuildid(info, r)
+ e2lib.logf(3, "result %20s: pbid(%s) bid(%s)",
+ r, e2tool.bid_display(pbid), e2tool.bid_display(bid))
+ end
+end
+
+function e2tool.flush_buildids(info)
+ for r, res in pairs(info.results) do
+ res.buildid = nil
+ res.pbuildid = nil
+ end
+end
+
+function e2tool.chrootgroupid(info, groupname)
+ local g = info.chroot.groups_byname[groupname]
+ if g.groupid then
+ return g.groupid
+ end
+ local hc = hash.hash_start()
+ hc:hash_line(g.name)
+ hc:hash_line(g.server)
+ for _,f in ipairs(g.files) do
+ hc:hash_line(f)
+ -- XXX hash each file?
+ end
+ e2lib.log(4, string.format("hash data for chroot group %s\n%s",
+ groupname, hc.data))
+ g.groupid = hc:hash_finish()
+ return g.groupid
+end
+
+function e2tool.calc_chrootids(info)
+ for _,grp in pairs(info.chroot.groups) do
+ e2tool.chrootgroupid(info, grp.name)
+ end
+end
+
+--return a table of environment variables valid for a result
+-- @param info the info table
+-- @param resultname string: name of a result
+-- @return table: environment variables valid for the result
+function e2tool.env_by_result(info, resultname)
+ local e = {}
+ -- take global variables first
+ for k,v in pairs(info.env) do
+ if type(v) == "string" or
+ type(v) == "number" then
+ e[k] = v
+ end
+ end
+ -- result specific variables override global ones
+ for k,v in pairs(info.env) do
+ if type(v) == "table" and
+ k == resultname then
+ for k1,v1 in pairs(v) do
+ e[k1] = v1
+ end
+ end
+ end
+ return e
+end
+
+--- envid: calculate a value represennting the environment for a result
+-- @param info the info table
+-- @param resultname string: name of a result
+-- @return string: envid value
+function e2tool.envid(info, resultname)
+ local e = e2tool.env_by_result(info, resultname)
+ local hc = hash.hash_start()
+ for k,v in pairs(e) do
+ hc:hash_line(string.format("%s=%s", k, v))
+ end
+ local envid = hc:hash_finish()
+ return envid
+end
+
+function e2tool.add_source_result(info, sourcename, source_set)
+ e2lib.log(3, string.format("adding source result for source %s",
+ sourcename))
+ local src = info.sources[sourcename]
+ local r = {}
+ r.name = string.format("src-%s", src.name)
+ r.sources = { src.name }
+ r.depends = {}
+ r.chroot = {}
+ r.chroot.groups = {}
+ r.pseudo_result = true
+ info.results[r.name] = r
+end
+
+function e2tool.add_source_results(info, source_set)
+ e2lib.log(4, "add source results")
+ for _, src in pairs(info.sources) do
+ e2tool.add_source_result(info, src.name)
+ end
+end
+
+function e2tool.check_source(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, e, re
+ if not src then
+ e = new_error("no source by that name: %s", sourcename)
+ return false, e
+ end
+ local e = new_error("in source: %s", sourcename)
+ if not src.type then
+ e2lib.warnf("WDEFAULT", "in source %s", sourcename)
+ e2lib.warnf("WDEFAULT", " type attribute defaults to `files'")
+ src.type = "files"
+ end
+ if not e2scm[src.type] then
+ e:append("unknown scm type: %s", src.type)
+ return false, e
+ end
+ rc, re = scm.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+function e2tool.check_sources(info)
+ local e = new_error("Error while checking sources")
+ local rc, re
+ for n,s in pairs(info.sources) do
+ rc, re = e2tool.check_source(info, n)
+ if not rc then
+ e:cat(re)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+function e2tool.check_licence(info, l)
+ local e = new_error("in licence: %s", l)
+ local lic = info.licences[l]
+ if not lic.server then
+ e:append("no server attribute")
+ end
+ if not lic.files then
+ e:append("no files attribute")
+ elseif not type(lic.files) == "table" then
+ e:append("files attribute is not a table")
+ else
+ for _,file in ipairs(lic.files) do
+ if not type(file) == "string" then
+ e:append("file list holds non-string element")
+ end
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true
+end
+
+function e2tool.check_licences(info)
+ local e = new_error("Error while checking licences")
+ local rc, re
+ for l, lic in pairs(info.licences) do
+ rc, re = e2tool.check_licence(info, l)
+ if not rc then
+ e:cat(re)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+function e2tool.check_workingcopies(info)
+ local e = new_error("Error while checking working copies")
+ local rc, re
+ for n,s in pairs(info.sources) do
+ rc, re = scm.check_workingcopy(info, n)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+function e2tool.check_results(info)
+ local e = new_error("Error while checking results")
+ local rc, re
+ for r,_ in pairs(info.results) do
+ rc, re = e2tool.check_result(info, r)
+ if not rc then
+ e:cat(re)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ for r,_ in pairs(info.results) do
+ rc, re = e2tool.check_collect_project(info, r)
+ if not rc then
+ e:cat(re)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- check result configuration
+-- @param info table: the info table
+-- @param resultname string: the result to check
+function e2tool.check_result(info, resultname)
+ local res = info.results[resultname]
+ local e = new_error("in result %s:", resultname)
+ if not res then
+ e:append("result does not exist: %s", resultname)
+ return false, e
+ end
+ if res.files then
+ e2lib.warnf("WDEPRECATED", "in result %s", resultname)
+ e2lib.warnf("WDEPRECATED",
+ " files attribute is deprecated and no longer used")
+ res.files = nil
+ end
+ if type(res.sources) == "nil" then
+ e2lib.warnf("WDEFAULT", "in result %s:", resultname)
+ e2lib.warnf("WDEFAULT", " sources attribute not configured." ..
+ "Defaulting to empty list")
+ res.sources = {}
+ elseif type(res.sources) == "string" then
+ e2lib.warnf("WDEPRECATED", "in result %s:", resultname)
+ e2lib.warnf("WDEPRECATED", " sources attribute is string. "..
+ "Converting to list")
+ res.sources = { res.sources }
+ end
+ if type(res.sources) ~= "table" then
+ e:append("sources attribute has wrong type")
+ end
+ for i,s in ipairs(res.sources) do
+ if type(i) ~= "number" then
+ e:append("invalid key in sources list")
+ end
+ if type(s) ~= "string" then
+ e:append("non-string element in sources list")
+ elseif not info.sources[s] then
+ e:append("source does not exist: %s", s)
+ end
+ end
+ if type(res.depends) == "nil" then
+ e2lib.warn("WDEFAULT", "in result %s: ", resultname)
+ e2lib.warn("WDEFAULT", " depends attribute not configured. " ..
+ "Defaulting to empty list")
+ res.depends = {}
+ elseif type(res.depends) == "string" then
+ e2lib.warnf("WDEPRECATED", "in result %s:", resultname)
+ e2lib.warnf("WDEPRECATED", " depends attribute is string. "..
+ "Converting to list")
+ res.depends = { res.depends }
+ end
+ if type(res.depends) ~= "table" then
+ e:append("depends attribute has wrong type")
+ end
+ for i,d in pairs(res.depends) do
+ if type(i) ~= "number" then
+ e:append("non-number key in depends list")
+ end
+ if type(d) ~= "string" then
+ e:append("non-string element in depends list")
+ elseif not info.results[d] then
+ e:append("dependency does not exist: %s", d)
+ end
+ end
+ if type(res.chroot) == "nil" then
+ e2lib.warnf("WDEFAULT", "in result %s:", resultname)
+ e2lib.warnf("WDEFAULT", " chroot groups not configured. " ..
+ "Defaulting to empty list")
+ res.chroot = {}
+ elseif type(res.chroot) == "string" then
+ e2lib.warnf("WDEPRECATED", "in result %s:", resultname)
+ e2lib.warnf("WDEPRECATED", " chroot attribute is string. "..
+ "Converting to list")
+ res.chroot = { res.chroot }
+ end
+ if type(res.chroot) ~= "table" then
+ e:append("chroot attribute has wrong type")
+ end
+ -- apply default chroot groups
+ for _,g in ipairs(info.chroot.default_groups) do
+ table.insert(res.chroot, g)
+ end
+ for i,g in pairs(res.chroot) do
+ if type(i) ~= "number" then
+ e:append("non-number key in chroot list")
+ end
+ if type(g) ~= "string" then
+ e:append("non-string element in chroot list")
+ elseif not info.chroot.groups_byname[g] then
+ e:append("chroot group does not exist: %s", g)
+ end
+ end
+ if not res.buildno then
+ res.bn = {}
+ res.buildno = "0"
+ end
+ local build_script = string.format("%s/res/%s/build-script", info.root,
+ resultname)
+ if not e2lib.isfile(build_script) then
+ e:append("build-script does not exist")
+ end
+ -- stop if we had an error, as the collect_project stuff depends
+ -- on a sane result structure
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- check collect_project configuration
+-- This function depends on sane result and source configurations.
+-- Run only after check_result() was run on all results.
+-- @param info table: the info table
+-- @param resultname string: the result to check
+function e2tool.check_collect_project(info, resultname)
+ local res = info.results[resultname]
+ local e = new_error("in result %s:", resultname)
+ if not res.collect_project then
+ -- insert empty tables, to avoid some conditionals in the code
+ res.collect_project_results = {}
+ res.collect_project_sources = {}
+ res.collect_project_chroot_groups = {}
+ res.collect_project_licences = {}
+ -- XXX store list of used chroot groups here, too, and use.
+ return true, nil
+ end
+ local d = res.collect_project_default_result
+ if not d then
+ e:append("collect_project_default_result is not set")
+ elseif type(d) ~= "string" then
+ e:append(
+ "collect_project_default_result is non-string")
+ elseif not info.results[d] then
+ e:append("collect_project_default_result is set to "..
+ "an invalid result: %s", d)
+ end
+ -- catch errors upon this point before starting additional checks.
+ if e:getcount() > 1 then
+ return false, e
+ end
+ res.collect_project_results = e2tool.dlist_recursive(info,
+ res.collect_project_default_result)
+ -- store a sorted list of required results
+ table.insert(res.collect_project_results,
+ res.collect_project_default_result)
+ table.sort(res.collect_project_results)
+ e2lib.warnf("WDEFAULT", "collect_project takes these results: %s",
+ table.concat(res.collect_project_results, ","))
+ -- store a sorted list of required sources, chroot groups and licences
+ local tmp_grp = {}
+ local tmp_src = {}
+ tmp_grp["base"] = true
+ for _,r in ipairs(res.collect_project_results) do
+ local res = info.results[r]
+ for _,s in ipairs(res.sources) do
+ tmp_src[s] = true
+ end
+ for _,g in ipairs(res.chroot) do
+ -- use the name as key here, to hide duplicates...
+ tmp_grp[g] = true
+ end
+ end
+ res.collect_project_sources = {}
+ for s,_ in pairs(tmp_src) do
+ -- and build the desired array
+ table.insert(res.collect_project_sources, s)
+ end
+ table.sort(res.collect_project_sources)
+ res.collect_project_chroot_groups = {}
+ for g,_ in pairs(tmp_grp) do
+ table.insert(res.collect_project_chroot_groups, g)
+ end
+ table.sort(res.collect_project_chroot_groups)
+ res.collect_project_licences = {}
+ for _,l in ipairs(info.licences_sorted) do
+ table.insert(res.collect_project_licences, l)
+ end
+ table.sort(res.collect_project_licences)
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- parse build numbers from a string and store to the build number table
+-- @param info: the info table
+-- @param s string: the string to parse
+-- @param build_numbers table: build number table (optional)
+-- @return bool
+-- @return nil, an error object on error
+function e2tool.string2bn(info, s, build_numbers)
+ e2lib.logf(4, "string2bn()")
+ if not build_numbers then
+ build_numbers = info.build_numbers
+ end
+ local rc
+ local re = new_error("error parsing build numbers:")
+ e2lib.log(3, "parsing build numbers")
+ local line = 0
+ for l in s:gmatch("[^\n]+") do
+ line = line + 1
+ local bn = {}
+ local r
+ r, bn.bid, bn.status, bn.num = l:match(
+ ("([-%w_]+)%s+(%x+)%s+(%S+)%s+(%d+)"))
+ if not r then
+ re:append("parse error in line %d", line)
+ return false, re
+ end
+ e2lib.logf(4, "%s %s %s %s", r, bn.bid, bn.status, bn.num)
+ local oldbn = build_numbers[r]
+ if oldbn and oldbn.num and oldbn.num ~= bn.num then
+ bn.oldnum = oldbn.num
+ end
+ build_numbers[r] = bn
+ end
+ return true, nil
+end
+
+--- serialize the build number table suitable for storage or network
+-- transport
+-- @param info: the info table
+-- @param build_numbers table: build number table (optional)
+-- @return s string: serialized build numbers, or nil
+-- @return nil, an error object on error
+function bn2string(info, build_numbers)
+ e2lib.logf(4, "bn2string()")
+ if not build_numbers then
+ build_numbers = info.build_numbers
+ end
+ local s = ""
+ for r,bn in pairs(build_numbers) do
+ e2lib.logf(4, "%s %s %s %s", r, bn.bid, bn.status, bn.num)
+ local s1 = string.format("%s %s %s %s\n",
+ r, bn.bid, bn.status, bn.num)
+ s = s .. s1
+ end
+ return s, nil
+end
+
+--- write the build number file
+-- @param info the info table
+-- @param file string: the build number file (optional)
+-- @param build_numbers table: build number table (optional)
+-- @return bool
+-- @return an error object on error
+function e2tool.buildnumber_write(info, file, build_numbers)
+ e2lib.logf(4, "e2tool.buildnumber_write()")
+ local rc, msg
+ if not file then
+ file = info.buildnumber_file
+ end
+ if not build_numbers then
+ build_numbers = info.build_numbers
+ end
+ local e = new_error("error writing build number file:")
+ e2lib.logf(3, "writing build numbers to %s", file)
+ local s, re = bn2string(info)
+ if not s then
+ e:cat(re)
+ return false, e
+ end
+ rc, re = e2lib.write_file(file, s)
+ if not rc then
+ e:cat(re)
+ return false, e
+ end
+ return true, nil
+end
+
+--- read the build number file into the buildnumber table
+-- @param info the info table
+-- @param file string: the build number file (optional)
+-- @param build_numbers table: build number table (optional)
+-- @return bool
+-- @return an error object on error
+function e2tool.buildnumber_read(info, file, build_numbers)
+ e2lib.logf(4, "e2tool.buildnumber_read()")
+ local rc, re, msg
+ if not file then
+ file = info.buildnumber_file
+ end
+ if not build_numbers then
+ build_numbers = info.build_numbers
+ end
+ local e = new_error("error reading build number file:")
+ e2lib.logf(3, "reading build-numbers from %s", file)
+ local s, re = e2lib.read_file(file)
+ if not s and e2lib.isfile(file) then
+ e:cat(re)
+ return false, e
+ elseif not s then
+ e2lib.warnf("WOTHER", "build number file does not exist")
+ s = ""
+ end
+ local rc, re = e2tool.string2bn(info, s, build_numbers)
+ if not rc then
+ e:cat(re)
+ return false, e
+ end
+ return true, nil
+end
+
+--- merge build numbers from the build number table to the results
+-- @param info table: the info table
+-- @return bool
+-- @return nil, an error object on failure
+function e2tool.buildnumber_mergetoresults(info)
+ e2lib.log(3, string.format("merging build numbers to results"))
+ local e = new_error("merging build numbers to results:")
+ for r, res in pairs(info.results) do
+ local bn = info.build_numbers[r]
+ if not bn then
+ e2lib.warnf("WOTHER",
+ "no build number entry for result: %s", r)
+ elseif res.pbuildid == bn.id then
+ e2lib.log(3, string.format(
+ "applying build number to result: %s [%s]",
+ r, bn.num))
+ res.buildno = bn.num
+ else
+ e:append("pseudo buildid mismatch in result %s", r)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- merge build numbers and pbid from the result to the build number table
+-- @param info table: the info table
+-- @return bool
+-- @return nil, an error object on failure
+function e2tool.buildnumber_mergefromresults(info)
+ e2lib.log(3, string.format("merging build numbers from results"))
+ for r, res in pairs(info.results) do
+ local bn = info.build_numbers[r]
+ if not bn then
+ e2lib.warnf("WOTHER",
+ "creating new build number entry for result: %s", r)
+ -- create a new entry
+ bn = {}
+ bn.status = "ok"
+ bn.num = res.buildno
+ info.build_numbers[r] = bn
+ end
+ bn.bid = e2tool.pbuildid(info, r)
+ e2lib.logf(4, "%s %s %s %s", r, tostring(bn.bid), bn.status, bn.num)
+ end
+ return true, nil
+end
+
+--- display buildnumbers
+-- @param build_numbers table: build number table
+-- @param loglevel (optional, default 2)
+-- @return nil
+function e2tool.buildnumber_display(build_numbers, loglevel)
+ if not loglevel then
+ loglevel = 2
+ end
+ e2lib.log(loglevel, "displaying build-number table:")
+ e2lib.logf(loglevel, "%-20s %-40s %2s %5s %-7s",
+ "result", "pbuildid", "st", "num", "old")
+ for r,bn in pairs(build_numbers) do
+ local changed = ""
+ if bn.oldnum then
+ changed = string.format("[%d]", bn.oldnum)
+ end
+ e2lib.logf(loglevel, "%-20s %40s %2s %5d %-7s",
+ r, bn.bid, bn.status, bn.num, changed)
+ end
+end
+
+--- request new build numbers from the server
+-- @param info
+-- @return bool
+-- @return an error object on failure
+function e2tool.buildnumber_request(info)
+ e2lib.log(3, "requesting build numbers from server")
+ local rc, re
+ local e = new_error("error requesting build numbers")
+ local tmpdir = e2lib.mktempdir()
+ local tmpreq = string.format("%s/build-number.req.tmp", tmpdir)
+ local tmpres = string.format("%s/build-number.res.tmp", tmpdir)
+ local curlflags = "--create-dirs --silent --show-error --fail"
+ local url = string.format(
+ "'%s?project=%s&user=%s&host=%s'",
+ e2lib.buildnumber_server_url, info.name, e2lib.osenv["USER"],
+ e2lib.hostname)
+ local args = string.format(
+ "%s " ..
+ "--header 'Content-type: text/plain' " ..
+ "--data-binary '@%s' %s -o %s",
+ curlflags,
+ tmpreq, url, tmpres)
+ rc, re = e2tool.buildnumber_write(info, tmpreq)
+ if not rc then
+ e:append(re)
+ return false, e
+ end
+ e2lib.log(3, "sending request")
+ rc, re = e2lib.curl(args)
+ if not rc then
+ e:append(re)
+ return false, e
+ end
+ rc, re = e2tool.buildnumber_read(info, tmpres)
+ if not rc then
+ e:append(re)
+ return false, e
+ end
+ e2lib.rmtempdir(tmpdir)
+ return true, nil
+end
+
+--- perform the buildnumber update without synchronizing to the server
+-- @param info
+-- @return bool
+-- @return an error object on failure
+function e2tool.buildnumber_request_local(info)
+ e2lib.log(3, "requesting build numbers locally")
+ local rc, re
+ local req -- the request
+ local sta -- the state
+ local res -- the response
+ local e = new_error("error in local buildnumber request")
+ -- compose the request
+ req = info.build_numbers
+ -- compose the state
+ sta = {}
+ rc, re = e2tool.buildnumber_read(info, nil, sta)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- run the update function locally
+ res = {}
+ rc, re = e2tool.buildnumber_update(sta, req, res)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- convert the result to a string
+ local s
+ s, re = bn2string(info, res)
+ if not s then
+ return false, e:cat(re)
+ end
+ -- convert the string back into the info structure
+ rc, re = e2tool.string2bn(info, s)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true
+end
+
+--- update buildnumbers - usable on server side, or in --no-sync mode on the
+-- client side
+-- @param state table: build number table state
+-- @param request table: build number table request
+-- @param response table: build number table response
+-- @return build number table
+function e2tool.buildnumber_update(state, request, response)
+ e2lib.log(4, "buildnumber_update()")
+ e2lib.log(4, "state:")
+ e2tool.buildnumber_display(state, 4)
+ e2lib.log(4, "request")
+ e2tool.buildnumber_display(request, 4)
+ for r,bn in pairs(request) do
+ local req = bn
+ local sta = state[r]
+ e2lib.logf(4, "checking status for %s", r)
+ if not sta then
+ sta = {}
+ sta.bid = req.bid
+ sta.num = 1
+ sta.status = "ok"
+ state[r] = sta
+ elseif sta.bid ~= req.bid or sta.num ~= req.num then
+ e2lib.logf(4, "increasing buildnumber for %s", r)
+ -- update status
+ sta.num = math.max(sta.num, req.num) + 1
+ sta.bid = req.bid
+ sta.status = "ok"
+ end
+ -- create the response
+ local res = {}
+ res.bid = sta.bid
+ res.num = sta.num
+ res.status = sta.status
+ response[r] = res
+ end
+ return true, nil
+end
+
+--- select the result and apply build options
+-- @param info
+-- @param r string: the result name
+-- @param force_rebuild bool
+-- @param request_buildno bool
+-- @param keep_chroot bool
+-- @param build_mode table: build mode policy
+-- @param playground bool
+-- @return nil
+function e2tool.select_result(info, r, force_rebuild, request_buildno, keep_chroot, build_mode, playground)
+ local res = info.results[r]
+ if not res then
+ e2lib.abort(string.format("selecting invalid result: %s", r))
+ end
+ res.selected = true
+ res.force_rebuild = force_rebuild
+ res.request_buildno = request_buildno
+ res.keep_chroot = keep_chroot
+ if build_mode then
+ res.build_mode = build_mode
+ end
+ res.playground = playground
+end
+
+
+--- select results based upon a list of results usually given on the
+-- command line. Parameters are assigned to all selected results.
+-- @param info the info structure
+-- @param results table: list of result names
+-- @param force_rebuild bool
+-- @param request_buildno bool
+-- @param keep_chroot bool
+-- @param build_mode table: build mode policy. Optional.
+-- @param playground bool
+-- @return bool
+-- @return an error object on failure
+function e2tool.select_results(info, results, force_rebuild, request_buildno, keep_chroot, build_mode, playground)
+ for _,r in ipairs(results) do
+ e2tool.select_result(info, r, force_rebuild, request_buildno,
+ keep_chroot, build_mode, playground)
+ end
+ return true, nil
+end
+
+--- print selection status for a list of results
+-- @param info
+-- @param results table: list of result names
+-- @return bool
+-- @return an error object on failure
+function e2tool.print_selection(info, results)
+ for _,r in ipairs(results) do
+ local e = new_error("error printing selected results")
+ local res = info.results[r]
+ if not res then
+ return false, e:append("no such result: %s", r)
+ end
+ local s = res.selected and "[ selected ]" or
+ "[dependency]"
+ local f = res.force_rebuild and "[force rebuild]" or ""
+ local b = res.request_buildno and "[request buildno]" or ""
+ local p = res.playground and "[playground]" or ""
+ e2lib.log(3, string.format(
+ "Selected result: %-20s %s %s %s %s",
+ r, s, f, b, p))
+ end
+ return true, nil
+end
+
+--- chdir to a directory relative to info.root
+-- @param info
+-- @param dir string: directory
+-- @return bool
+-- @return an error object on failure
+function e2tool.lcd(info, dir)
+ local e = new_error("chdir failed")
+ local abspath = string.format("%s/%s", info.root, dir)
+ local rc, re = e2lib.chdir(abspath)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true
+end
+
+--- check for configuration syntax compatibility
+-- @param info
+-- @return bool
+-- @return an error object on failure
+function e2tool.display_config_syntax_compat(info)
+ local e = new_error("displaying configuration syntax compatibilitly failed")
+ for _,m in ipairs(info.config_syntax_compat) do
+ print(m)
+ end
+ return true
+end
+
+--- check for configuration syntax compatibility
+-- @param info
+-- @return bool
+-- @return an error object on failure
+function e2tool.check_config_syntax_compat(info)
+ local e = new_error("checking configuration syntax compatibilitly failed")
+ local l, re = e2lib.read_line(info.config_syntax_file)
+ if not l then
+ return false, e:cat(re)
+ end
+ for _,m in ipairs(info.config_syntax_compat) do
+ m = string.format("^%s$", m)
+ if l:match(m) then
+ return true, nil
+ end
+ end
+ return false, e:append("configuration syntax mismatch")
+end
+
+--- read chroot configuration
+-- @param info
+-- @return bool
+-- @return an error object on failure
+function e2tool.read_chroot_config(info)
+ local e = new_error("reading chroot config failed")
+ local t = {}
+ local rc, re = e2tool.load_user_config(info, info.chroot_config_file,
+ t, "chroot", "e2chroot")
+ if not rc then
+ return false, e:cat(re)
+ end
+ if type(t.chroot) ~= "table" then
+ return false, e:append("chroot configuration table not available")
+ end
+ if type(t.chroot.groups) ~= "table" then
+ return false, e:append("chroot.groups configuration is not a table")
+ end
+ if type(t.chroot.default_groups) ~= "table" then
+ return false, e:append("chroot.default_groups is not a table")
+ end
+ --- chroot config
+ -- @class table
+ -- @name info.chroot
+ -- @field default_groups chroot groups used in any result
+ -- @field groups chroot groups in configuration order
+ -- @field groups_byname chroot groups keyed by name
+ -- @field groups_sorted chroot groups sorted by name
+ info.chroot = {}
+ info.chroot.default_groups = t.chroot.default_groups
+ info.chroot.groups = t.chroot.groups
+ info.chroot.groups_byname = {}
+ info.chroot.groups_sorted = {}
+ for _,grp in pairs(info.chroot.groups) do
+ if grp.group then
+ e:append("in group: %s", grp.group)
+ e:append(" `group' attribute is deprecated. Replace by `name'")
+ return false, e
+ end
+ if not grp.name then
+ return false, e:append("`name' attribute is missing in a group")
+ end
+ local g = grp.name
+ table.insert(info.chroot.groups_sorted, g)
+ if info.chroot.groups_byname[g] then
+ return false, e:append("duplicate chroot group name: %s", g)
+ end
+ info.chroot.groups_byname[g] = grp
+ end
+ table.sort(info.chroot.groups_sorted)
+ return true
+end
+
+--- check chroot config
+-- @param chroot
+-- @return bool
+-- @return an error object on failure
+function e2tool.check_chroot_config(info)
+ local e = new_error("error validating chroot configuration")
+ for g,grp in pairs(info.chroot.groups) do
+ if not grp.server then
+ e:append("in group: %s", grp.name)
+ e:append(" `server' attribute missing")
+ elseif not info.servers[grp.server] then
+ e:append("in group: %s", grp.name)
+ e:append(" no such server: %s", grp.server)
+ end
+ if (not grp.files) or (#grp.files) == 0 then
+ e:append("in group: %s", grp.name)
+ e:append(" list of files is empty")
+ else
+ for _,l in ipairs(grp.files) do
+ if type(l) ~= "string" then
+ e:append("in group: %s", grp.name)
+ e:append(" file list contains non-string element")
+ end
+ end
+ end
+ end
+ if (not info.chroot.default_groups) or #info.chroot.default_groups == 0 then
+ e:append(" `default_groups' attribute is missing or empty list")
+ else
+ for _,g in ipairs(info.chroot.default_groups) do
+ if not info.chroot.groups_byname[g] then
+ e:append(" unknown group in default groups list: %s", g)
+ end
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true
+end
+
+function e2tool.load_source_config(info)
+ local e = new_error("error loading source configuration")
+ info.sources = {}
+ for src in e2lib.directory(info.root .. "/src") do
+ local list, re
+ local path = string.format("src/%s/config", src)
+ local types = { "e2source", }
+ list, re = e2tool.load_user_config2(info, path, types)
+ if not list then
+ return false, e:cat(re)
+ end
+ for _,item in ipairs(list) do
+ local name = item.data.name
+ if not name and #list == 1 then
+ e2lib.warnf("WDEFAULT", "`name' attribute missing in source config.")
+ e2lib.warnf("WDEFAULT", " Defaulting to directory name")
+ item.data.name = src
+ name = src
+ end
+ if not name then
+ return false, e:append("`name' attribute missing in source config")
+ end
+ if not name:match("^[-._0-9a-zA-Z]+$") then
+ e:append("invalid source name: %s")
+ e:append("only digits, alphabetic characters, and `-', `_' and `.' "..
+ "are allowed")
+ return false, e
+ end
+ if info.sources[name] then
+ return false, e:append("duplicate source: %s", name)
+ end
+ item.data.configfile = item.filename
+ info.sources[name] = item.data
+ end
+ end
+ return true, nil
+end
+
+function e2tool.load_result_config(info)
+ local e = new_error("error loading result configuration")
+ info.results = {}
+ for res in e2lib.directory(info.root .. "/res") do
+ local list, re
+ local path = string.format("res/%s/config", res)
+ local types = { "e2result", }
+ list, re = e2tool.load_user_config2(info, path, types)
+ if not list then
+ return false, e:cat(re)
+ end
+ if #list ~= 1 then
+ return false, e:append("only one result allowed per config file")
+ end
+ for _,item in ipairs(list) do
+ local name = item.data.name
+ if name and name ~= res then
+ e:append("`name' attribute does not match configuration path")
+ return false, e
+ end
+ item.data.name = res
+ name = res
+ if not name:match("^[-._0-9a-zA-Z]+$") then
+ e:append("invalid result name: %s")
+ e:append("only digits, alphabetic characters, and `-', `_' and `.' "..
+ "are allowed")
+ return false, e
+ end
+ if info.results[name] then
+ return false, e:append("duplicate result: %s", name)
+ end
+ item.data.configfile = item.filename
+ info.results[name] = item.data
+ end
+ end
+ return true, nil
+end
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- fetch-sources - Retrieve sources for project -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-fetch-sources <source> ...
+
+fetch all sources for a project, provided they are not fetched yet.
+up-to-dateness is not checked for sources which are already fetched.
+]]
+
+-- --all (--scm)
+-- --source select sources by source names
+-- --result select sources by result names
+-- --chroot select chroot files
+-- --files select files sources
+-- --scm select sources using scm systems
+-- --git
+-- --cvs
+-- --svn
+--
+-- --fetch, --cache fetch selected sources
+-- --update update selected sources
+
+local e = new_error()
+
+e2option.flag("all", "select all sources, even files sources")
+e2option.flag("chroot", "select chroot files")
+e2option.flag("files", "select files sources")
+e2option.flag("scm", "select all scm sources")
+e2option.flag("git", "select scm sources")
+e2option.flag("cvs", "select cvs sources")
+e2option.flag("svn", "select svn sources")
+e2option.flag("fetch", "fetch selected sources (default)")
+e2option.flag("update", "update selected source")
+e2option.flag("source", "select sources by source names (default)")
+e2option.flag("result", "select sources by result names")
+
+local opts = e2option.parse(arg)
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+local rc, re = e2tool.check_project_info(info)
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-fetch-sources")
+
+if not (opts.fetch or opts.update) then
+ opts.fetch = true
+ e2lib.warn("WOTHER", "Selecting fetch by default")
+end
+if opts.all then
+ e2lib.warn("WOTHER", "--all selects all sources, even files sources")
+end
+if #opts.arguments > 0 then
+ opts.selection = true
+end
+if not (opts.scm or opts.files or opts.chroot or opts.selection
+ or opts.git or opts.cvs or opts.svn) then
+ e2lib.warn("WOTHER", "Selecting scm sources by default")
+ opts.scm = true
+end
+if opts.scm then
+ opts.git = true
+ opts.cvs = true
+ opts.svn = true
+end
+local select_type = {}
+if opts["git"] then
+ select_type["git"] = true
+end
+if opts["svn"] then
+ select_type["svn"] = true
+end
+if opts["cvs"] then
+ select_type["cvs"] = true
+end
+if opts["files"] then
+ select_type["files"] = true
+end
+
+--- cache chroot files
+-- @param info the info table
+-- @return bool
+-- @return nil, an error string on error
+function cache_chroot(info)
+ for _,c in ipairs(info.chroot) do
+ for _,file in ipairs(c.files) do
+ local rc, e = cache.cache_file(info.cache, c.server, file, {})
+ if not rc then
+ return false, "caching file failed"
+ end
+ end
+ end
+ return true, nil
+end
+
+--- fetch and upgrade sources
+-- @param info the info table
+-- @param opts the option table
+-- @param sel table of selected results
+-- @return bool
+-- @return nil, an error string on error
+function fetch_sources(info, opts, sel)
+ local rc1 = true -- global return code
+ local nfail = 0 -- failure counter
+ local e = new_error() -- no message yet, append the summary later on
+
+ -- fetch
+ for _, s in pairs(info.sources) do
+ local has_wc = scm.has_working_copy(info, s.name)
+ local wc_avail = e2scm[s.type].working_copy_available(info, s.name)
+ if opts.fetch and sel[s.name] then
+ if wc_avail then
+ e2lib.log(1, "working copy for " .. s.name .. " is already available")
+ else
+ e2lib.log(1, "fetching working copy for source " .. s.name)
+ local rc, re = scm.fetch_source(info, s.name)
+ if not rc then
+ e2lib.log(4, string.format("fetching source failed: %s",
+ s.name))
+ e:cat(re)
+ end
+ end
+ end
+ end
+
+ -- update
+ for _, s in pairs(info.sources) do
+ local has_wc = scm.has_working_copy(info, s.name)
+ local wc_avail = e2scm[s.type].working_copy_available(info, s.name)
+ if opts.update and has_wc and sel[s.name] then
+ if not wc_avail then
+ e2lib.log(1, string.format("working copy for %s is not available",
+ s.name))
+ else
+ e2lib.log(1, "updating working copy for " .. s.name)
+ local rc, re = scm.update(info, s.name)
+ if not rc then
+ e2lib.log(4, string.format("updating working copy failed: %s",
+ s.name))
+ e:cat(re)
+ end
+ end
+ end
+ end
+ local nfail = e:getcount()
+ if nfail > 0 then
+ e:append("There were errors fetching %d sources", nfail)
+ return false, e
+ end
+ return true, nil
+end
+
+local sel = {} -- selected sources
+
+if #opts.arguments > 0 then
+ for _, x in pairs(opts.arguments) do
+ if info.sources[x] and not opts.result then
+ e2lib.log(3, "is regarded as source: " .. x)
+ sel[x] = x
+ elseif info.results[x] and opts.result then
+ e2lib.log(3, "is regarded as result: " .. x)
+ local res = info.results[x]
+ for _, s in ipairs(res.sources) do
+ sel[s] = s
+ end
+ else
+ e2lib.abort("is neither known source nor result: " .. x)
+ end
+ end
+elseif opts["all"] then
+ -- select all sources
+ for s,src in pairs(info.sources) do
+ sel[s] = s
+ end
+end
+
+-- select all sources by scm type
+for s, src in pairs(info.sources) do
+ if select_type[src.type] then
+ sel[s] = s
+ end
+end
+
+
+for _, s in pairs(sel) do
+ e2lib.logf(2, "selecting source: %s" , s)
+ local src = info.sources[s]
+ if not src then
+ e:append("selecting invalid source: %s", s)
+ end
+end
+if e:getcount() > 0 then
+ e2lib.abort(e)
+end
+
+if opts.chroot then
+ e2lib.log(2, "caching chroot files")
+ local rc, re = cache_chroot(info)
+ if not rc then
+ e:append("Error: Caching chroot files failed")
+ e:cat(re)
+ end
+end
+
+if opts.scm or opts.files or opts.git or opts.cvs or opts.svn or
+ opts.selection then
+ e2lib.log(2, "fetching sources...")
+ local rc, re = fetch_sources(info, opts, sel)
+ if not rc then
+ e:cat(re)
+ end
+end
+
+--e2hook.run_hook(info, "fetch-sources", srcs)
+--e2hook.run_hook(info, "tool-finish", nil, "e2-fetch-sources")
+
+if e:getcount() > 0 then
+ e2lib.abort(e)
+end
+
+e2lib.finish()
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- files.lua - non-SCM operations -*- Lua -*-
+--
+-- See e2scm.lua for more information about these operations.
+
+
+local files = {}
+
+--- validate source configuration, log errors to the debug log
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+function files.validate_source(info, sourcename)
+ local rc1 = true -- the return value
+ local rc, e = generic_validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ e = new_error("in source %s:", sourcename)
+ e:setcount(0)
+ local src = info.sources[ sourcename ]
+ if not src.file then
+ e:append("%s: source has no `file' attribute", sourcename)
+ end
+ if src.file then
+ for _,f in pairs(src.file) do
+ -- catch deprecated configuration
+ if f.name then
+ e:append("source has file entry with `name' attribute")
+ end
+ if (not f.licences) and src.licences then
+ f.licences = src.licences
+ end
+ if (not f.server) and src.server then
+ f.server = src.server
+ end
+ if not f.licences then
+ e:append("source has file entry without `licences' attribute")
+ end
+ for _,l in ipairs(f.licences) do
+ if not info.licences[l] then
+ e:append("invalid licence assigned to file: %s", l)
+ end
+ end
+ if not f.server then
+ e:append("source has file entry without `server' attribute")
+ end
+ if f.server and (not info.servers[f.server]) then
+ e:append("invalid server: %s", f.server)
+ end
+ if not f.location then
+ e:append("source has file entry without `location' attribute")
+ end
+ if not (f.unpack or f.copy or f.patch) then
+ e:append("source has file entry without `unpack, copy or patch' " ..
+ "attribute")
+ end
+ if f.location and (not f.checksum_file) then
+ f.checksum_file = string.format("%s%s", f.location,
+ info.default_checksum_file_suffix)
+ e2lib.warnf("WDEFAULT", "in source %s:", sourcename)
+ e2lib.warnf("WDEFAULT", " file: %s:%s", f.server, f.location)
+ e2lib.warnf("WDEFAULT", " checksum_file defaulting to %s",
+ f.checksum_file)
+ end
+ end
+ end
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+function files.get_revision_id(info, sourcename, sourceset)
+ local rc, e
+ rc, e = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ local function files_hash_iterator(files)
+ local i = nil
+ local function nextfile(f)
+ i = next(f, i)
+ return i and files[i].hash
+ end
+ return nextfile, files
+ end
+ local s = info.sources[sourcename]
+ if not s.fhash then
+ -- store hash for each file
+ for _,f in pairs(s.file) do
+ f.hash, e = e2tool.read_hash_file(info, f.server, f.checksum_file)
+ if not f.hash then
+ e2lib.log(3, string.format(
+ "hash file not available for %s:%s. Hashing on-the-fly",
+ f.server, f.location))
+ f.hash, e = e2tool.hash_file(info, f.server, f.location)
+ end
+ if not f.hash then
+ e2lib.log(1, e)
+ e2lib.abort(string.format("fetching file %s:%s", f.server, f.location))
+ end
+ end
+ s.fhash = e2lib.compute_hash(files_hash_iterator, s.file)
+ end
+ return s.fhash
+end
+
+--- cache files for a source
+-- @param info the info structure
+-- @param sourcename name of the source
+-- @return bool
+-- @return nil, an error string on error
+function files.cache_source(info, sourcename)
+ local rc, e
+ rc, e = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ local s = info.sources[sourcename]
+ -- cache all files for this source
+ for i,f in pairs(s.file) do
+ e2lib.log(4, string.format("files.cache_source: caching file %s:%s",
+ f.server, f.location))
+ local flags = { cache = true }
+ if f.server ~= info.root_server_name then
+ local rc, e = cache.cache_file(info.cache, f.server, f.location, flags)
+ if not rc then
+ return false, e
+ end
+ local rc, e = cache.cache_file(info.cache, f.server, f.checksum_file,
+ flags)
+ if not rc then
+ e2lib.log(3,
+ estring(e, string.format("%s:%s", f.server, f.checksum_file)))
+ -- this is not fatal
+ end
+ else
+ e2lib.log(4, string.format("not caching %s:%s (stored locally)",
+ f.server, f.location))
+ end
+ end
+ return true, nil
+end
+
+function files.fetch_source(info, sourcename)
+ local rc, re
+ local e = new_error("fetching source failed: %s", sourcename)
+ rc, re = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = files.cache_source(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+function files.working_copy_available(info, sourcename)
+ local rc, e
+ rc, e = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ return false
+end
+
+function files.has_working_copy(info, sourcename)
+ local rc, e
+ rc, e = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ return false
+end
+
+--- prepare a files source
+-- @return bool
+-- @return nil, maybe an error string on error
+function files.prepare_source(info, sourcename, sourceset, buildpath)
+ local rc, re
+ local e = new_error("error preparing source: %s", sourcename)
+ rc, re = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local symlink = nil
+ e2lib.log(4, string.format("prepare source: %s", sourcename))
+ local s = info.sources[sourcename]
+ for _,file in ipairs(info.sources[sourcename].file) do
+ if file.unpack then
+ local cache_flags = { cache = true }
+ local rc, re = cache.cache_file(info.cache, file.server, file.location,
+ cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local path, re = cache.file_path(info.cache, file.server, file.location,
+ cache_flags)
+ if not path then
+ return false, e:cat(re)
+ end
+ local y = e2lib.howtounpack(path, path, buildpath)
+ if not y or e2lib.callcmd_capture(y) ~= 0 then
+ return false, e:append("failed to unpack: %s", path)
+ end
+ if not symlink then
+ symlink = buildpath .. "/" .. sourcename
+ if file.unpack ~= sourcename then
+ if not e2util.symlink(file.unpack, symlink) then
+ return false, e:append("cannot create symlink: %s -> %s", symlink,
+ file.unpack)
+ end
+ end
+ end
+ else
+ if not symlink then
+ symlink = buildpath .. "/" .. sourcename
+ local rc, re = e2lib.mkdir(symlink, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ if file.patch then
+ local cache_flags = { cache = true }
+ local rc, re = cache.file_path(info.cache, file.server, file.location,
+ cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local path, re = cache.file_path(info.cache, file.server,
+ file.location, cache_flags)
+ if not path then
+ return false, e:append(re)
+ end
+ local args = string.format("-p '%s' -d '%s' -i '%s'", file.patch,
+ symlink, path)
+ rc, re = e2lib.patch(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ elseif file.copy then
+ local fcdirname = e2lib.dirname(file.copy)
+ local fcbasename = e2lib.basename(file.copy)
+ local destination = string.format("%s/%s/%s", buildpath, sourcename,
+ file.copy)
+ local destdir, destname
+ -- emulate the cp behaviour to feed the cache.fetch_file interface
+ -- correctly, that does not allow ambiguities
+ if e2lib.isdir(destination) then
+ destdir = destination
+ destname = nil
+ else
+ destdir = string.format("%s/%s/%s", buildpath, sourcename,
+ fcdirname)
+ destname = fcbasename
+ if not e2lib.mkdir(destdir, "-p") then
+ e2lib.abort(string.format(
+ "can't create destination directory: %s", destdir))
+ end
+ end
+ local rc, re = cache.fetch_file(info.cache, file.server, file.location,
+ destdir, destname, {})
+ if not rc then
+ return false, e:cat(re)
+ end
+ else
+ e2lib.abort(string.format("missing destiny for file %s (%s)",
+ file.location, file.server))
+ end
+ end
+ e2hook.run_hook(info, "files-prepare-source",
+ {source=sourcename, file=file.base, path=buildpath},
+ "e2-build")
+ end
+ return true, nil
+end
+
+--- create a table of lines for display
+-- @param info the info structure
+-- @param sourcename string
+-- @return a table, nil on error
+-- @return an error string on failure
+function files.display(info, sourcename)
+ local rc, e
+ rc, e = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ local src = info.sources[sourcename]
+ local display = {}
+ display[1] = string.format("type = %s", src.type)
+ local i = 2
+ for _,f in pairs(src.file) do
+ display[i] = string.format("file = %s:%s", f.server, f.location)
+ i = i + 1
+ end
+ for _,l in ipairs(src.licences) do
+ display[i] = string.format("licence = %s", l)
+ i = i + 1
+ end
+ if src.sourceid then
+ display[i] = string.format("sourceid = %s", src.sourceid)
+ i = i + 1
+ end
+ return display
+end
+
+--- calculate an id for a source
+-- @param info
+-- @param sourcename
+-- @param sourceset
+-- @return string: the source id, nil on error
+-- @return an error string on error
+function files.sourceid(info, sourcename, sourceset)
+ local rc, e
+ rc, e = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ local src = info.sources[sourcename]
+ if src.sourceid then
+ return src.sourceid
+ end
+ -- sourceset is ignored for files sources
+ local hc = hash.hash_start()
+ hash.hash_line(hc, src.name)
+ hash.hash_line(hc, src.type)
+ for _,l in ipairs(src.licences) do
+ hash.hash_line(hc, l)
+ local licenceid, re = e2tool.licenceid(info, l)
+ if not licenceid then
+ return nil, re
+ end
+ hash.hash_line(hc, licenceid)
+ end
+ for _,f in ipairs(src.file) do
+ f.hash, e = e2tool.read_hash_file(info, f.server,
+ f.checksum_file)
+ if not f.hash then
+ if f.server ~= info.root_server_name then
+ e2lib.warn("WOTHER", string.format(
+ "in source %s: " ..
+ "checksum file not available",
+ src.name))
+ e2lib.warn("WOTHER", string.format(
+ " file: %s:%s", f.server, f.location))
+ e2lib.warn("WOTHER", string.format(
+ " checksum_file: %s", f.checksum_file))
+ end
+ f.hash, e = e2tool.hash_file(info, f.server,
+ f.location)
+ end
+ if not f.hash then
+ return nil, e
+ end
+ hash.hash_line(hc, f.checksum_file)
+ hash.hash_line(hc, f.location)
+ hash.hash_line(hc, f.hash)
+ hash.hash_line(hc, f.server)
+ hash.hash_line(hc, tostring(f.unpack))
+ hash.hash_line(hc, tostring(f.patch))
+ hash.hash_line(hc, tostring(f.copy))
+ end
+ e2lib.log(4, string.format("hash data for source %s\n%s", src.name,
+ hc.data))
+ src.sourceid = hash.hash_finish(hc)
+ return src.sourceid
+end
+
+-- export the source to a result structure
+function files.toresult(info, sourcename, sourceset, directory)
+ local rc, re
+ local e = new_error("converting result failed")
+ rc, re = files.validate_source(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local s = info.sources[sourcename]
+ local makefile = "makefile" -- name of the makefile
+ local source = "source" -- directory to store source files in
+ local fname = string.format("%s/%s", directory, makefile)
+ local f, msg = io.open(fname, "w")
+ if not f then
+ return false, e:cat(msg)
+ end
+ f:write(string.format(
+ ".PHONY:\tplace\n\n"..
+ "place:\n"))
+ for _,file in ipairs(s.file) do
+ e2lib.log(4, string.format("export file: %s", file.location))
+ local destdir = string.format("%s/%s", directory, source)
+ local destname = nil
+ e2lib.mkdir(destdir, "-p")
+ local rc, re = cache.fetch_file(info.cache, file.server,
+ file.location, destdir, destname, {})
+ if not rc then
+ return false, e:cat(re)
+ end
+ local c = e2lib.howtounpack(
+ string.format("%s/%s", destdir,
+ e2lib.basename(file.location)),
+ string.format("%s/%s", source,
+ e2lib.basename(file.location)),
+ string.format("$(BUILD)"))
+ if c then
+ f:write(string.format("\t%s\n", c))
+ end
+ if file.unpack then
+ f:write(string.format(
+ "\tln -s %s $(BUILD)/%s\n", file.unpack, sourcename))
+ end
+ if file.copy then
+ f:write(string.format(
+ "\tmkdir -p \"$(BUILD)/%s\"\n"..
+ "\tcp \"%s/%s\" \"$(BUILD)/%s/%s\"\n",
+ sourcename,
+ source, e2lib.basename(file.location), sourcename,
+ file.copy))
+ end
+ if file.patch then
+ f:write(string.format(
+ "\tpatch -p%s -d \"$(BUILD)/%s\" "..
+ "-i \"$(shell pwd)/%s/%s\"\n",
+ file.patch, sourcename, source,
+ e2lib.basename(file.location)))
+ end
+ -- write licences
+ local destdir = string.format("%s/licences", directory)
+ local fname = string.format("%s/%s.licences", destdir,
+ e2lib.basename(file.location))
+ local licence_list = table.concat(file.licences, "\n") .. "\n"
+ rc, re = e2lib.mkdir(destdir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.write_file(fname, licence_list)
+ if not rc then
+ return false, e:cat(re)
+ end
+ e2lib.log(4, string.format("export file: %s done",
+ file.location))
+ end
+ f:close()
+ return true, nil
+end
+
+function files.check_workingcopy(info, sourcename)
+ return true, nil
+end
+
+e2scm.register("files", files)
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+--- git.lua - Git-specific SCM operations -*- Lua -*-
+--
+-- See e2scm.lua for more information about these operations.
+
+local git = {}
+
+--- git branch wrapper
+-- get the current branch
+-- @param gitdir
+-- @return string: the branch name, nil on error
+-- @return string: nil, or an error string on error
+function git_branch_get(gitdir)
+ -- git branch
+ local cmd = string.format("GIT_DIR=\"%s\" git branch", gitdir)
+ local p = io.popen(cmd, "r")
+ local branch = nil
+ while true do
+ local line = p:read()
+ if not line then
+ break
+ end
+ local x
+ -- search for a line matching '* <branchname>'
+ x, branch = line:match("^(\* )(%S*)$")
+ if x and branch then
+ break
+ end
+ end
+ p:close()
+ if not branch then
+ return branch, nil, "git branch: can't get current branch"
+ end
+ return branch, nil
+end
+
+--- return a value suitable for buildid computation, i.e. the commit-id
+-- @param info the info table
+-- @param source string: the source name
+-- @param sourceset string: the sourceset
+-- @return string: the commit id, nil on error
+-- @return nil on success, an error string on error
+function git.get_revision_id(info, source, sourceset)
+ -- XXX reading remote tag via cache is silly. Use git tools.
+ local sourcename = source
+ local rc, re
+ local e = new_error("getting revision id failed for source: %s", source)
+ local s = info.sources[source]
+ rc, re = git.validate_source(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = scm.working_copy_available(info, sourcename)
+ if not rc then
+ return false, e:append("working copy is not available")
+ end
+ rc, re = scm.check_workingcopy(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local p = info.root .. "/" .. s.working .. "/.git/refs/"
+ local id, fr, gitdir, ref
+ if sourceset == "branch" or
+ (sourceset == "lazytag" and s.tag == "^") then
+ gitdir = string.format("%s/%s/.git", info.root, s.working)
+ ref = string.format("refs/heads/%s", s.branch)
+ id, e = generic_git.git_rev_list1(gitdir, ref)
+ -- error checking delayed to end of function
+ if not id then
+ fr = "can't get commit id for branch: " .. s.branch
+ end
+ elseif sourceset == "tag" or
+ (sourceset == "lazytag" and s.tag ~= "^") then
+ gitdir = string.format("%s/%s/.git", info.root, s.working)
+ ref = string.format("refs/tags/%s", s.tag)
+ id, e = generic_git.git_rev_list1(gitdir, ref)
+ -- error checking delayed to end of function
+ -- XXX remote access remains missing for now. Probably doesn't make
+ -- sense here anyway, but in some other function like
+ -- check-if-remote-has-our-tag-too(), or force-push-our-tag-prior-
+ -- to-release-tagging()
+ else
+ e2lib.abort("not an scm sourceset: " .. sourceset)
+ end
+ if not id then
+ fr = string.format("can't get commit id for ref %s from repository %s",
+ ref, s.working)
+ end
+ return id, not id and fr
+end
+
+function git.cache_source(info, sourcename)
+ -- noop, return success
+ return true, nil
+end
+
+--- apply default values where possible and a source configuration is
+-- incomplete
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+-- @return an error object on failure
+function source_apply_default_licences(info, sourcename)
+ local e = new_error("applying default licences failed.")
+ local src = info.sources[ sourcename ]
+ if src.licences_default_applied then
+ return true
+ end
+ src.licences_default_applied = true
+ if not src.licences and src.licence then
+ e2lib.warnf("WDEPRECATED", "in source %s:", src.name)
+ e2lib.warnf("WDEPRECATED",
+ " licence attribute is deprecated. Replace by licences.")
+ src.licences = src.licence
+ end
+ if not src.licences then
+ e2lib.warnf("WDEFAULT", "in source %s:", src.name)
+ e2lib.warnf("WDEFAULT",
+ " licences attribute missing. Defaulting to empty list.")
+ src.licences = {}
+ elseif type(src.licences) == "string" then
+ e2lib.warnf("WDEPRECATED", "in source %s:", src.name)
+ e2lib.warnf("WDEPRECATED",
+ " licences attribute is not in table format. Converting.")
+ src.licences = { src.licences }
+ end
+ for _,l in ipairs(src.licences) do
+ if not info.licences[l] then
+ e:append("unknown licence: %s", l)
+ return false, e
+ end
+ end
+ return true
+end
+
+-- XXX generics: Move to some other place
+--- validate source configuration
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+-- @return an error object on failure
+function generic_validate_source(info, sourcename)
+ local src = info.sources[ sourcename ]
+ local rc, re
+ local e
+ if not src then
+ return false, new_error("invalid source: %s", sourcename)
+ end
+ e = new_error("in source %s:", sourcename)
+ rc, re = source_apply_default_licences(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ if not src.type then
+ e:append("source has no `type' attribute")
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- do some consistency checks required before using sources
+-- @param info
+-- @param sourcename string: source name
+-- @param require_workingcopy bool: return error if the workingcopy is missing
+-- @return bool
+-- @return an error object on failure
+function check(info, sourcename, require_workingcopy)
+ local rc, re
+ rc, re = scm.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ rc, re = scm.working_copy_available(info, sourcename)
+ if (not rc) and require_workingcopy then
+ return false, new_error("working copy is not available")
+ end
+ rc, re = scm.check_workingcopy(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ return true, nil
+end
+
+--- apply default values where possible and a git source configuration is
+-- incomplete
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+-- @return an error object on failure
+function source_apply_default_working(info, sourcename)
+ local src = info.sources[ sourcename ]
+ if src.working_default_applied then
+ return true
+ end
+ src.working_default_applied = true
+ local src_working_default = string.format("in/%s", sourcename)
+ if src.working and src.working ~= src_working_default then
+ e2lib.warnf("WPOLICY", "in source %s:", src.name)
+ e2lib.warnf("WPOLICY", " configuring non standard working direcory")
+ elseif src.working then
+ e2lib.warnf("WHINT", "in source %s:", src.name)
+ e2lib.warnf("WHINT", " no need to configure working directory")
+ else
+ src.working = string.format("in/%s", sourcename)
+ e2lib.warnf("WDEFAULT", "in source %s:", src.name)
+ e2lib.warnf("WDEFAULT",
+ " `working' attribute missing. Defaulting to '%s'.", src.working)
+ end
+ return true
+end
+
+--- validate source configuration, log errors to the debug log
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+-- @return an error object on error
+function git.validate_source(info, sourcename)
+ local rc, re = generic_validate_source(info, sourcename)
+ if not rc then
+ -- error in generic configuration. Don't try to go on.
+ return false, re
+ end
+ local src = info.sources[ sourcename ]
+ local e = new_error("in source %s:", sourcename)
+ rc, re = source_apply_default_working(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ e:setcount(0)
+ -- catch deprecated attributes
+ if src.remote then
+ e:append("source has deprecated `remote' attribute")
+ end
+ if not src.server then
+ e:append("source has no `server' attribute")
+ end
+ if src.server and (not info.servers[src.server]) then
+ e:append("invalid server: %s", src.server)
+ end
+ if not src.licences then
+ e:append("source has no `licences' attribute")
+ end
+ if not src.branch then
+ e:append("source has no `branch' attribute")
+ end
+ if type(src.tag) ~= "string" then
+ e:append("source has no `tag' attribute or tag attribute has wrong type")
+ end
+ if not src.location then
+ e:append("source has no `location' attribute")
+ end
+ if not src.working then
+ e:append("source has no `working' attribute")
+ end
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+--- update a working copy
+-- @param info the info structure
+-- @param sourcename string
+-- @return bool
+-- @return an error object
+function git.update(info, sourcename)
+ local src = info.sources[ sourcename ]
+ local rc, re
+ local e = new_error("updating source failed")
+ rc, re = scm.working_copy_available(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local gitwc = string.format("%s/%s", info.root, src.working)
+ local gitdir = string.format("%s/%s/.git", info.root, src.working)
+ e2lib.logf(2, "updating %s [%s]", src.working, src.branch)
+ rc, re = e2tool.lcd(info, src.working)
+ if not rc then
+ return false, e:append("working copy not available")
+ end
+ rc, re = e2lib.git(nil, "fetch") -- git fetch is safe
+ if not rc then
+ return false, e:append(re)
+ end
+ e:append("fetch succeeded")
+
+ -- setup the branch tracking its remote. This fails if the branch exists,
+ -- but that's fine.
+ local args
+ args = string.format("--track '%s' 'origin/%s'", src.branch, src.branch)
+ rc, re = e2lib.git(nil, "branch", args)
+
+ -- sanity checks:
+ -- must be on configured branch
+ local branch, re = git_branch_get(gitdir)
+ if not branch then
+ return false, e:append(re)
+ end
+ if branch ~= src.branch then
+ e2lib.warnf("WOTHER", "not on configured branch. Skipping 'git pull'")
+ return true, nil
+ end
+ rc, re = e2tool.lcd(info, src.working)
+ if not rc then
+ return false, e:append("working copy not available")
+ end
+ rc, re = e2lib.git(nil, "pull")
+ if not rc then
+ return false, e:append(re)
+ end
+ return true, nil
+end
+
+--- fetch a git source
+-- @param info the info structure
+-- @param sourcename string
+-- @return bool
+-- @return nil on success, an error string on error
+function git.fetch_source(info, sourcename)
+ local src = info.sources[ sourcename ]
+ local rc, re
+ local e = new_error("fetching source failed: %s", sourcename)
+ rc, re = git.validate_source(info, sourcename)
+ if not rc then
+ return false, e:append(re)
+ end
+ local wrk = info.root .. "/" .. src.working
+ e2lib.log(2, string.format("cloning %s:%s [%s]",
+ src.server, src.location, src.branch))
+ local skip_checkout = e2lib.git_skip_checkout
+ rc, re = generic_git.git_clone_from_server(info.cache, src.server,
+ src.location, wrk, skip_checkout)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- check for the branch, and checkout if it's not yet there after cloning.
+ local ref = string.format("refs/heads/%s", src.branch)
+ local gitdir = string.format("%s/.git", wrk)
+ local rc, re = generic_git.git_rev_list1(gitdir, ref)
+ if not rc then
+ local track = true
+ local start_point = string.format("origin/%s", src.branch)
+ local rc, re = generic_git.git_branch_new1(wrk, track, src.branch,
+ start_point)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local rc, re = generic_git.git_checkout1(wrk, src.branch)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+end
+
+--- prepare a git source
+-- @param info the info structure
+-- @param sourcename string
+-- @return bool
+-- @return nil on success, an error string on error
+function git.prepare_source(info, sourcename, sourceset, buildpath)
+ local src = info.sources[ sourcename ]
+ local rc, re, e
+ local e = new_error("preparing git sources failed")
+ rc, re = check(info, sourcename, true)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local gitdir = info.root .. "/" .. src.working .. "/.git/"
+ if sourceset == "branch" or
+ (sourceset == "lazytag" and src.tag == "^") then
+ local rev, re = git.get_revision_id(info, sourcename, sourceset)
+ if not rev then
+ return false, e:cat(re)
+ end
+ gitdir = string.format("%s/%s/.git", info.root, src.working)
+ local git = string.format("GIT_DIR=\"%s\" "..
+ "git archive --format=tar --prefix=\"%s/\" \"refs/heads/%s\"",
+ gitdir, sourcename, src.branch)
+ local tar = string.format("tar -C \"%s\" -xf -", buildpath)
+ local re = e2lib.callcmd_pipe({git, tar})
+ if re then
+ return false, e:cat(re)
+ end
+ elseif sourceset == "tag" or
+ (sourceset == "lazytag" and src.tag ~= "^") then
+ local rev, re = git.get_revision_id(info, sourcename, sourceset)
+ if not rev then
+ return false, e:cat(re)
+ end
+ gitdir = string.format("%s/%s/.git", info.root, src.working)
+ local git = string.format("GIT_DIR=\"%s\" "..
+ "git archive --format=tar --prefix=\"%s/\" \"refs/tags/%s\"",
+ gitdir, sourcename, src.tag)
+ local tar = string.format("tar -C \"%s\" -xf -", buildpath)
+ local re = e2lib.callcmd_pipe({git, tar})
+ if re then
+ return false, e:cat(re)
+ end
+ elseif sourceset == "working-copy" then
+ -- warn for empty working-copy
+ local working = string.format("%s/%s", info.root, src.working)
+ local d = e2util.directory(working, false)
+ if #d == 0 then
+ e2lib.warnf("WOTHER", "in result: %s", src.name)
+ e2lib.warnf("WOTHER", "working copy seems empty")
+ end
+ local dir = string.format("%s/%s", buildpath, sourcename)
+ local rc, re = e2lib.mkdir(dir, "-p")
+ if not rc then
+ return false, re
+ end
+ local tar = transport.get_tool("tar")
+ local tarflags = transport.get_tool_flags("tar")
+ local cmd1 = string.format("%s %s -c -C '%s/%s' --exclude '.git' .", tar,
+ tarflags, info.root, src.working)
+ local cmd2 = string.format("%s %s -x -C '%s/%s'", tar, tarflags, buildpath,
+ sourcename)
+ local r = e2lib.callcmd_pipe({ cmd1, cmd2 })
+ if r then e2lib.abort(r) end
+ else e2lib.abort("invalid sourceset: ", sourceset)
+ end
+ return true, nil
+end
+
+--- check if a working copy for a git repository is available
+-- @param info the info structure
+-- @param sourcename string
+-- @return bool
+-- @return sometimes an error string, when ret. false. XXX interface cleanup.
+function git.working_copy_available(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re
+ local e = new_error("checking if working copy is available for source %s",
+ sourcename)
+ rc, re = git.validate_source(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local gitwc = info.root .. "/" .. src.working
+ local rc = e2lib.isdir(gitwc)
+ return rc, nil
+end
+
+function git.has_working_copy(info, sname)
+ return true
+end
+
+function git.tag_available(info, sourcename, tag)
+ local src = info.sources[sourcename]
+ local rc, e
+ rc, e = check(info, sourcename, true)
+ if not rc then
+ return false, e
+ end
+ local gitdir = string.format("%s/%s/.git", info.root, src.working)
+ local ref = string.format("refs/tags/%s", tag)
+ local id, e = generic_git.git_rev_list1(gitdir, ref)
+ if not id then
+ return false
+ end
+ return true
+end
+
+--- turn server:location into a git-style url
+-- @param c table: a cache
+-- @param server string: server name
+-- @param location string: location
+-- @return string: the git url, or nil
+-- @return an error object on failure
+function git.git_url(c, server, location)
+ local e = new_error("translating server:location to git url")
+ local rurl, re = cache.remote_url(c, server, location)
+ if not rurl then
+ return nil, e:cat(re)
+ end
+ local u, re = url.parse(rurl)
+ if not u then
+ return nil, e:cat(re)
+ end
+ local g, re = generic_git.git_url1(u)
+ if not g then
+ return nil, e:cat(re)
+ end
+ return g, nil
+end
+
+function git.git_remote_add(c, lserver, llocation, name, rserver, rlocation)
+ e2lib.log(4, string.format("%s, %s, %s, %s, %s, %s",
+ tostring(c), tostring(lserver), tostring(llocation),
+ tostring(name), tostring(rserver), tostring(rlocation)))
+ local rurl, e = cache.remote_url(c, rserver, rlocation)
+ if not rurl then
+ e2lib.abort(e)
+ end
+ local lurl, e = cache.remote_url(c, lserver, llocation)
+ if not lurl then
+ e2lib.abort(e)
+ end
+ local rc, e = generic_git.git_remote_add1(lurl, rurl, name)
+ if not rc then
+ e2lib.abort(e)
+ end
+ return true, nil
+end
+
+--- create a new git source repository
+-- @param c cache table
+-- @param lserver string: local server
+-- @param llocation string: working copy location on local server
+-- @param rserver string: remote server
+-- @param rlocation string: repository location on remote server
+-- @param flags: table of flags
+-- @return bool
+-- @return nil, or an error string on error
+function git.new_git_source(c, lserver, llocation, rserver, rlocation, flags)
+ local rc, re
+ local e = new_error("setting up new git repository failed")
+ local lserver_url, re = cache.remote_url(c, lserver, llocation)
+ if not lserver_url then
+ return false, e:cat(re)
+ end
+ local lurl, re = url.parse(lserver_url)
+ if not lurl then
+ return false, e:cat(re)
+ end
+ local rc = e2lib.mkdir(string.format("/%s", lurl.path), "-p")
+ if not rc then
+ return false, e:cat("can't create path to local git repository")
+ end
+ rc = generic_git.git_init_db(c, lserver, llocation)
+ if not rc then
+ return false, e:cat("can't initialize local git repository")
+ end
+ rc = git.git_remote_add(c, lserver, llocation, "origin",
+ rserver, rlocation)
+ if not rc then
+ return false, e:cat("git remote add failed")
+ end
+ rc = e2lib.chdir("/"..lurl.path)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local targs = {
+ string.format("'branch.master.remote' 'origin'"),
+ string.format("'branch.master.merge' 'refs/heads/master'"),
+ }
+ for _,args in ipairs(targs) do
+ rc, re = e2lib.git(".", "config", args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ rc = generic_git.git_init_db(c, rserver, rlocation)
+ if not rc then
+ return false, e:cat("can't initialize remote git repository")
+ end
+ return true, nil
+end
+
+--- create a table of lines for display
+-- @param info the info structure
+-- @param sourcename string
+-- @return a table, nil on error
+-- @return an error string on failure
+function git.display(info, sourcename)
+ local src = info.sources[sourcename]
+ local rc, re
+ local e = new_error("display source information failed")
+ rc, re = git.validate_source(info, sourcename)
+ if not rc then
+ return nil, e:cat(re)
+ end
+ -- try to calculte the sourceid, but do not care if it fails.
+ -- working copy might be unavailable
+ scm.sourceid(info, sourcename, "tag")
+ scm.sourceid(info, sourcename, "branch")
+ local rev_tag = ""
+ local rev_branch = ""
+ if src.commitid["tag"] then
+ rev_tag = string.format("[%s...]", src.commitid["tag"]:sub(1,8))
+ end
+ if src.commitid["branch"] then
+ rev_branch = string.format("[%s...]", src.commitid["branch"]:sub(1,8))
+ end
+ local display = {}
+ display[1] = string.format("type = %s", src.type)
+ display[2] = string.format("branch = %-15s %s", src.branch, rev_branch)
+ display[3] = string.format("tag = %-15s %s", src.tag, rev_tag)
+ display[4] = string.format("server = %s", src.server)
+ display[5] = string.format("location = %s", src.location)
+ display[6] = string.format("working = %s", src.working)
+ local i = 8
+ for _,l in ipairs(src.licences) do
+ display[i] = string.format("licence = %s", l)
+ i = i + 1
+ end
+ for _,sourceset in pairs({"tag", "branch"}) do
+ if src.sourceid and src.sourceid[sourceset] then
+ local id = src.sourceid[sourceset]
+ local s = string.format("sourceid[%s]", sourceset)
+ display[i] = string.format("%-11s= %s", s, id)
+ i = i + 1
+ end
+ end
+ i = i + 1
+ return display
+end
+
+--- calculate an id for a source
+-- @param info
+-- @param sourcename
+-- @return string: the sourceid, or nil
+-- @return an error string
+function git.sourceid(info, sourcename, sourceset)
+ local src = info.sources[sourcename]
+ if not src.sourceid then
+ src.sourceid = {}
+ src.sourceid["working-copy"] = "working-copy"
+ src.commitid = {}
+ end
+ if src.sourceid[sourceset] then
+ return src.sourceid[sourceset]
+ end
+ src.commitid[sourceset], e = git.get_revision_id(info, sourcename, sourceset)
+ if not src.commitid[sourceset] then
+ return nil, e
+ end
+ local hc = hash.hash_start()
+ hash.hash_line(hc, src.name)
+ hash.hash_line(hc, src.type)
+ for _,l in ipairs(src.licences) do
+ hash.hash_line(hc, l)
+ local licenceid, re = e2tool.licenceid(info, l)
+ if not licenceid then
+ return nil, re
+ end
+ hash.hash_line(hc, licenceid)
+ end
+ -- git specific
+ --hash.hash_line(hc, src.branch)
+ --hash.hash_line(hc, src.tag)
+ hash.hash_line(hc, src.server)
+ hash.hash_line(hc, src.location)
+ hash.hash_line(hc, src.working)
+ hash.hash_line(hc, src.commitid[sourceset])
+ e2lib.log(4, string.format("hash data for source %s\n%s", src.name,
+ hc.data))
+ src.sourceid[sourceset] = hash.hash_finish(hc)
+ return src.sourceid[sourceset]
+end
+
+local function sourceset2ref(info, sourcename, sourceset)
+ local src = info.sources[sourcename]
+ local rc, e
+ rc, e = git.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ if sourceset == "branch" or
+ (sourceset == "lazytag" and src.tag == "^") then
+ return string.format("refs/heads/%s", src.branch)
+ elseif sourceset == "tag" or
+ (sourceset == "lazytag" and src.tag ~= "^") then
+ return string.format("refs/tags/%s", src.tag)
+ end
+ return nil, "invalid sourceset"
+end
+
+function git.toresult(info, sourcename, sourceset, directory)
+ local rc, re
+ local e = new_error("converting result")
+ rc, re = check(info, sourcename, true)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local src = info.sources[sourcename]
+ local makefile = "makefile"
+ local source = "source"
+ local sourcedir = string.format("%s/%s", directory, source)
+ e2lib.mkdir(sourcedir, "-p")
+ local archive = string.format("%s.tar.gz", src.name)
+ local ref = sourceset2ref(info, sourcename, sourceset)
+ -- git archive --format=tar <ref> | gzip > <tarball>
+ local cmd = string.format(
+ "cd %s/%s && git archive --format=tar --prefix=\"%s/\" %s | "..
+ "gzip > %s/%s",
+ info.root, src.working, sourcename, ref, sourcedir, archive)
+ local rc, re = e2lib.callcmd_log(cmd)
+ if rc ~= 0 then
+ return false, e:cat(re)
+ end
+ local fname = string.format("%s/%s", directory, makefile)
+ local f, msg = io.open(fname, "w")
+ if not f then
+ return false, e:cat(msg)
+ end
+ f:write(string.format(
+ ".PHONY:\tplace\n\n"..
+ "place:\n"..
+ "\ttar xzf \"%s/%s\" -C \"$(BUILD)\"\n",
+ source, archive))
+ f:close()
+ -- write licences
+ local destdir = string.format("%s/licences", directory)
+ local fname = string.format("%s/%s.licences", destdir, archive)
+ local licence_list = table.concat(src.licences, "\n") .. "\n"
+ rc, re = e2lib.mkdir(destdir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.write_file(fname, licence_list)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+function git.check_workingcopy(info, sourcename)
+ local rc, re
+ local e = new_error("checking working copy failed")
+ e:append("in source %s (git configuration):", sourcename)
+ e:setcount(0)
+ rc, re = git.validate_source(info, sourcename)
+ if not rc then
+ return nil, re
+ end
+ local src = info.sources[sourcename]
+ local query, expect, res
+ local rc, re = scm.working_copy_available(info, sourcename)
+ if not rc then
+ e2lib.warnf("WOTHER", "in source %s: ", sourcename)
+ e2lib.warnf("WOTHER", " working copy is not available")
+ return true, nil
+ end
+ local gitdir = string.format("%s/%s/.git", info.root, src.working)
+ -- check if branch exists
+ rc, re = e2tool.lcd(info, src.working)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local ref = string.format("refs/heads/%s", src.branch)
+ rc, re = generic_git.git_rev_list1(nil, ref)
+ if not rc then
+ e:append("branch not available: %s", src.branch)
+ return false, e:cat(re)
+ end
+ -- git config branch.<branch>.remote == "origin"
+ query = string.format("branch.%s.remote", src.branch)
+ expect = string.format("origin")
+ res, re = generic_git.git_config(gitdir, query)
+ if not res then
+ return false, e:cat(re)
+ end
+ if res ~= expect then
+ e:append("%s is not \"origin\"", query)
+ end
+ -- git config remote.origin.url == server:location
+ query = string.format("remote.origin.url")
+ expect, re = git.git_url(info.cache, src.server, src.location)
+ if not expect then
+ return false, e:cat(re)
+ end
+ res, re = generic_git.git_config(gitdir, query)
+ if not res then
+ return false, e:cat(re)
+ end
+ local function remove_trailing_slashes(s)
+ while s:sub(#s) == "/" do
+ s = s:sub(1, #s-1)
+ end
+ return s
+ end
+ res = remove_trailing_slashes(res)
+ expect = remove_trailing_slashes(expect)
+ if res ~= expect then
+ e:append("%s does not match the configuration", query)
+ end
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+e2scm.register("git", git)
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ linux32.c
+*/
+
+
+//#include <linux/personality.h>
+//#undef personality
+#include <sys/personality.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+/* taken from x86_64 headers */
+#define PER_LINUX32 0x0008
+
+int main(int argc, char *argv[])
+{
+ int persona = PER_LINUX32;
+ int rc;
+ /* need to change personality on x86_64 systems, but no harm on i386 */
+ rc = personality(persona);
+ if (rc<0) {
+ fprintf(stderr, "Cannot set %x personality: %s\n", persona,
+ strerror(errno));
+ exit(1);
+ }
+ if(argc < 2) {
+ exit(0);
+ }
+ execvp(argv[1], &argv[1]);
+ fprintf(stderr, "Cannot exec: %s\n", strerror(errno));
+ exit(1);
+}
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- loads required libraries
+
+require("e2util_local")
+require("luafile_ll_local")
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- lookup-server -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-lookup-server <server>
+
+lookup the given server and print its absolute path
+]]
+
+local opts = e2option.parse(arg)
+if #opts.arguments ~= 1 then e2option.usage(1) end
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-lookup-server")
+
+local server, islocal = e2tool.lookup_server(info, opts.arguments[1])
+if not server then e2options.usage(1) end
+print(server)
+
+e2hook.run_hook(info, "tool-finish", nil, "e2-lookup-server")
+e2lib.finish()
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- ls-project - show project information -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-ls-project
+
+show project information
+]]
+
+policy.register_commandline_options()
+e2option.flag("dot", "generate dot(1) graph")
+e2option.flag("dot-sources", "generate dot(1) grapth with sources included")
+e2option.flag("swap", "swap arrow directions")
+
+local opts = e2option.parse(arg)
+
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+local rc, re = e2tool.check_project_info(info)
+if not rc then
+ e2lib.abort(re)
+end
+
+--e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-ls-project")
+
+local function pempty(s1, s2, s3)
+ print(string.format(" %s %s %s", s1, s2, s3))
+end
+local function p0(s1, s2, v)
+ print(string.format("%s", v))
+end
+local function p1(s1, s2, v)
+ print(string.format(" o--%s", v))
+end
+local function p2(s1, s2, v)
+ print(string.format(" %s o--%s", s1, v))
+end
+local function p3(s1, s2, k, v)
+ if v then
+ -- remove leading spaces, that allows easier string
+ -- append code below, where collecting multiple items
+ while v:sub(1,1) == " " do
+
+ v = v:sub(2)
+ end
+ print(string.format(" %s %s o--%-10s = %s", s1, s2, k, v))
+ else
+ print(string.format(" %s %s o--%s", s1, s2, k))
+ end
+end
+
+local function p3t(s1, s2, k, t)
+ local col = tonumber(e2lib.osenv["COLUMNS"])
+ local header1 = string.format(" %s %s o--%-10s =", s1, s2, k)
+ local header2 = string.format(" %s %s %-10s ", s1, s2, "")
+ local header = header1
+ local l = nil
+ local i = 0
+ for _,v in ipairs(t) do
+ i = i + 1
+ if l then
+ if (l:len() + v:len() + 1) > col then
+ print(l)
+ l = nil
+ end
+ end
+ if not l then
+ l = string.format("%s %s", header, v)
+ else
+ l = string.format("%s %s", l, v)
+ end
+ header = header2
+ end
+ if l then
+ print(l)
+ end
+end
+
+if opts.dot or opts["dot-sources"] then
+ local arrow = "->"
+ print("digraph \"" .. info.name .. "\" {")
+ for _,r in ipairs(info.results_sorted) do
+ local res = info.results[r]
+ local deps = e2tool.dlist(info, r)
+ if #deps > 0 then
+ for _, dep in pairs(deps) do
+ if opts.swap then
+ print(string.format(" \"%s\" %s \"%s\"", dep, arrow, r))
+ else
+ print(string.format(" \"%s\" %s \"%s\"", r, arrow, dep))
+ end
+ end
+ else
+ print(string.format(" \"%s\"", r))
+ end
+ for _, src in ipairs(res.sources) do
+ if opts["dot-sources"] then
+ if opts.swap then
+ print(string.format(" \"%s-src\" %s \"%s\"", src, arrow, r))
+ else
+ print(string.format(" \"%s\" %s \"%s-src\"", r, arrow, src))
+ end
+ end
+ end
+ end
+ for _, s in ipairs(info.sources_sorted) do
+ if opts["dot-sources"] then
+ print(string.format(" \"%s-src\" [label=\"%s\", shape=box]", s, s))
+ end
+ end
+ print("}")
+ e2lib.finish()
+ e2hook.run_hook(info, "tool-finish", nil, "e2-ls-project")
+end
+
+--------------- project name
+local s1 = "|"
+local s2 = "|"
+p0(s1, s2, info.name)
+
+--------------- servers
+local s1 = "|"
+local s2 = "|"
+p1(s1, s2, "servers")
+local len = #info.servers_sorted
+for _,s in ipairs(info.servers_sorted) do
+ local srv = info.servers[s]
+ len = len - 1
+ if len > 0 then
+ s2 = "|"
+ else
+ s2 = " "
+ end
+ p2(s1, s2, s)
+ for k,v in pairs(srv) do
+ if k ~= "name" then
+ p3(s1, s2, k, tostring(v))
+ end
+ end
+end
+print(" |")
+
+--------------------- sources
+local s1 = "|"
+local s2 = " "
+p1(s1, s2, "src")
+local len = #info.sources_sorted
+for _,s in ipairs(info.sources_sorted) do
+ local src = info.sources[s]
+ len = len - 1
+ if len == 0 then
+ s2 = " "
+ else
+ s2 = "|"
+ end
+ p2(s1, s2, src.name)
+ local t, re = scm.display(info, src.name)
+ if not t then
+ e2lib.abort(re)
+ end
+ for _,line in pairs(t) do
+ p3(s1, s2, line)
+ end
+end
+
+--------------------- results
+s1 = "|"
+s2 = " "
+s3 = " "
+pempty(s1, s2, s3)
+s2 = " "
+p1(s1, s2, "res")
+local len = #info.results_sorted
+for _,r in ipairs(info.results_sorted) do
+ local res = info.results[r]
+ p2(s1, s2, r)
+ len = len - 1
+ if len == 0 then
+ s2 = " "
+ else
+ s2 = "|"
+ end
+ p3t(s1, s2, "sources", res.sources)
+ p3t(s1, s2, "depends", res.depends)
+ if res.collect_project then
+ p3(s1, s2, "collect_project", "enabled")
+ p3(s1, s2, "collect_project_default_result",
+ res.collect_project_default_result)
+ end
+end
+
+--------------------- licences
+s1 = "|"
+s2 = " "
+s3 = " "
+pempty(s1, s2, s3)
+s2 = "|"
+p1(s1, s2, "licences")
+llen = #info.licences_sorted
+for _,l in pairs(info.licences_sorted) do
+ local lic = info.licences[l]
+ llen = llen - 1
+ if llen == 0 then
+ s2 = " "
+ end
+ p2(s1, s2, l)
+ p3(s1, s2, "server", lic.server)
+ for _,file in ipairs(lic.files) do
+ p3(s1, s2, "file", file)
+ end
+end
+
+--------------------- chroot
+local s1 = "|"
+local s2 = " "
+local s3 = " "
+pempty(s1, s2, s3)
+p1(s1, s2, "chroot groups")
+local s1 = " "
+local s2 = "|"
+local len = #info.chroot.groups_sorted
+for _,g in ipairs(info.chroot.groups_sorted) do
+ local grp = info.chroot.groups_byname[g]
+ len = len - 1
+ if len == 0 then
+ s2 = " "
+ end
+ p2(s1, s2, grp.name, grp.name)
+ p3(s1, s2, "server", grp.server)
+ p3t(s1, s2, "files", grp.files)
+ if grp.groupid then
+ p3(s1, s2, "groupid", grp.groupid)
+ end
+end
+
+e2hook.run_hook(info, "tool-finish", nil, "e2-ls-project")
+e2lib.finish()
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+SUBDIRS =
+TOPLEVEL = ../..
+
+include $(TOPLEVEL)/make.vars
+
+CLEAN_FILES = *~
+
+.PHONY: all install-local clean
+
+all:
+
+install-local:
+ install -m 755 -d $(LOCALMAKDIR)
+ install -m644 README $(LOCALMAKDIR)/README
+ install -m644 makefile.template $(LOCALMAKDIR)/makefile
+ install -m644 makefile.res.template $(LOCALMAKDIR)/makefile.res
+ install -m644 makefile.collect_project $(LOCALMAKDIR)/Makefile
+ install -m755 detect_tool $(LOCALMAKDIR)/
+ install -m644 buildall.sh build.sh $(LOCALMAKDIR)/
+
+clean:
+ rm -f $(CLEAN_FILES)
--- /dev/null
+#######################################################################
+# #
+# e2factory README for an extracted project Package #
+# #
+#######################################################################
+
+
+1.0 Introduction
+
+This package has been extracted from a "e2factory" project. "e2
+factory" is a build system targeted for embedded Linux-based systems
+and provides 100% reproducible software-builds through the use of a
+sophisticated hashing algorithm and utilization of a chroot(1) build
+environment.
+
+"e2factory" provides a command called "e2-prepare-cargo", which
+extracts all relevant files and tools from a project and allows
+building a software package outside of this build environment.
+
+"e2-prepare-cargo" exports all necessary binaries, scripts,
+configuration-files and sources to rebuild a project independently
+from "e2factory", while conforming to the GPL license (the build
+system is currently closed source). The extracted package is not
+intended for easy modification and restructuring of the project as
+this does not match the idea for guaranteed reproducibility. If you
+plan to heavily modify the project, to add some specific software or
+to do any other changes please consider using a full version of "e2
+factory". Contact emlix for further information.
+
+emlix GmbH
+http://www.emlix.com
+info@emlix.com
+Fon +49 551 30664-0
+Fax +49 551 30664-11
+Bahnhofsallee 1b
+37081 Göttingen
+Germany
+
+
+2.0 Directory Layout before building
+
+After unpacking the you will find the following five directories:
+
+* chroot
+* .e2
+* proj
+* res
+* src
+
+2.1 Directory "chroot"
+
+The "chroot" directory contains all needed tools to build the project,
+including a whole Linux environment with will be used inside the build
+environment. All tools are in binary form and archived in tarballs. Do
+not extract them manually. This will be done fully automatically.
+
+2.2. Directory ".e2"
+
+This hidden directory contains all necessary binary tools of "e2
+factory".
+
+2.3 Directory "proj"
+
+The directory "proj" contains the configuration of the project. The
+"e2factory" configuration files were automatically translated into
+Makefiles inside this directory.
+
+2.4 Directory "res"
+
+The "res" directory contains the configuration of each result of the
+project, where a result is something produced from a collection of
+sources during the build process. Each result has its own sub
+directory. The sub directories contain a buildscript and a
+makefile. The makefile was automatically generated from the "e2
+factory" project.
+
+2.5 Directory "src"
+
+This directory contains all sources of the project, either in the form
+of tarballs and patches or as a collection of flat files. The tarballs
+either come directly from the open source projects or were exported
+from the repositories used at emlix. The build-environment is
+completely self-contained and does not use any software-repository nor
+any source code management (SCM) tools. The full "e2factory" lets you
+access git, subversion and cvs repositories to build from. Any other
+can easily be integrated.
+
+
+3.0 Building it
+
+Rebuilding the project is just one call:
+
+ user@pc:~ # sudo make -C proj/
+
+3.1 Build a single result
+
+By typing
+
+ user@pc:~ # sudo make -C proj/ <res>
+
+only a the result <res> will be built and of course any of its
+dependencies.
+
+
+4.0 Directory Layout after building
+
+After successfully building the project you will find the following
+layout of directories:
+
+* build
+* chroot
+* .e2
+* out
+* proj
+* res
+* src
+
+Most directories are already described above.
+
+4.1 Directory "build"
+
+The "build" directory contains the build environment of every result.
+Sources may be changed at build/<result>/tmp/e2/build/<source> if KEEP=1
+was set in previouse make call. The latter subdir has to be touched to
+make "make" detect the changes.
+
+ user@pc:~ # touch build/<result>/tmp/e2/build/<source>
+
+This is because the makefiles generated during preparation
+for cargo do not know about the dependencies for a specific source, it
+just compares timestamps.
+
+4.2 Directory "out"
+
+In the "out" directory all build results are stored for each
+result. It depends on the configuration for each result what exactly
+is stored here. You emlix support will tell you which result is the
+final one for your project. Most probably more than one result is of
+interest.
+
+4.3 Change directories
+
+Both subdir locations, "build" and "out", may be overridden by make
+variables. To build at "/tmp/build", store results at "../out" and
+keep the chroot environments for manual use, do:
+
+ user@pc:~ # make -C proj BUILD=/tmp/build OUT=${PWD}/../out
+
+Note the use of the shell variable PWD to avoid relative paths.
+
+
+5.0 System requirements
+
+5.1 Size / disk usage
+
+Building a package uses quite a lot of disk storage, it is always good
+to have several gigabytes free. The exact amount depends on the size
+of the project.
+
+5.2 Installed Software
+
+The package can be build on almost any current linux system on a x86
+32-Bit or 64-Bit PC. Make sure a native compiler and make is
+installed. A meta-package called "build-essentials" should be
+installed on a debian/ubuntu based system.
--- /dev/null
+#!/bin/bash
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+set -e
+RESULT="$1"
+source res/$RESULT/config # set CHROOT, DEPEND, SOURCE
+source proj/config
+if [ "$chroot_arch" == "x86_64" ] &&
+ [ $(uname -m) != "x86_64" ] ; then
+ echo >&2 "need x86_64 host to build this project"
+ exit 1
+fi
+chroot_path=/tmp/e2build.$RESULT.cp
+e2-su-2.2 remove_chroot_2_2 $chroot_path >/dev/null 2>&1 || true
+mkdir -p $chroot_path
+touch $chroot_path/emlix-chroot
+e2-su-2.2 set_permissions_2_2 $chroot_path
+# install chroot groups
+for g in $CHROOT ; do
+ for f in $(ls chroot/$g) ; do
+ e2-su-2.2 extract_tar_2_2 $chroot_path "tar.gz" chroot/$g/$f
+ true
+ done
+done
+# install sources
+for s in $SOURCE ; do
+ dst=$chroot_path/tmp/e2/build
+ mkdir -p $dst
+ make BUILD=$dst -C src/$s place
+done
+# install deps
+for d in $DEPEND ; do
+ dst=$chroot_path/tmp/e2/dep
+ mkdir -p $dst
+ cp -v -r out/$d $dst/
+done
+# install result stuff
+mkdir -p $chroot_path/tmp/e2/{script,init,env,dep,build,out,root}
+cp -v proj/init/* $chroot_path/tmp/e2/init
+cp -v res/$RESULT/{build-driver,buildrc,build-script} \
+ $chroot_path/tmp/e2/script/
+cp -v res/$RESULT/{builtin,env} $chroot_path/tmp/e2/env/
+if [ "$chroot_arch" == "x86_32" ] ; then
+ ./linux32 \
+ e2-su-2.2 chroot_2_2 $chroot_path \
+ /bin/bash -e -x /tmp/e2/script/build-driver </dev/null
+elif [ "$chroot_arch" == "x86_64" ] ; then
+ e2-su-2.2 chroot_2_2 $chroot_path \
+ /bin/bash -e -x /tmp/e2/script/build-driver </dev/null
+fi
+# fetch result
+rm -fr out/$RESULT
+mkdir -p out/$RESULT
+cp -v $chroot_path/tmp/e2/out/* out/$RESULT/
+e2-su-2.2 remove_chroot_2_2 $chroot_path
+exit 0
--- /dev/null
+#!/bin/bash
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+set -e
+mkdir -p log
+source proj/config
+if [ "$chroot_arch" == "x86_64" ] &&
+ [ $(uname -m) != "x86_64" ] ; then
+ echo >&2 "Error: Need x86_64 host to build this project"
+ exit 1
+fi
+for result in $(cat resultlist) ; do
+ echo >&2 "logging to log/$result.log"
+ bash -x ./build.sh $result >log/$result.log 2>&1
+done
--- /dev/null
+#!/bin/bash
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Help finding tools used by the root code by enhancing the standard search
+# path. This will do in almost all cases.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin:$PATH" which "$1" && exit 0
+echo >&2 "missing Tool: $1"
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+
+GROUP = ebs
+
+CC = gcc
+CFLAGS = -Wall -g
+
+DETECT_TOOL = ./detect_tool
+ENV_TOOL = $(shell $(DETECT_TOOL) env)
+CHROOT_TOOL = $(shell $(DETECT_TOOL) chroot)
+TAR_TOOL = $(shell $(DETECT_TOOL) tar)
+CHOWN_TOOL = $(shell $(DETECT_TOOL) chown)
+RM_TOOL = $(shell $(DETECT_TOOL) rm)
+
+E2_SU_CFLAGS = -D CHROOT_TOOL="\"$(CHROOT_TOOL)\"" \
+ -D TAR_TOOL="\"$(TAR_TOOL)\"" \
+ -D CHOWN_TOOL="\"$(CHOWN_TOOL)\"" \
+ -D RM_TOOL="\"$(RM_TOOL)\""
+
+default: build
+
+install-suid: e2-su-2.2
+ install -m 4754 -g $(GROUP) e2-su-2.2 $(BINDIR)
+
+install: e2-su-2.2
+ install -m 755 e2-su-2.2 $(BINDIR)
+
+linux32: linux32.c
+
+e2-su-2.2: e2-su-2.2.c
+ $(CC) $(CFLAGS) $(E2_SU_CFLAGS) $(LDFLAGS) $< -o $@
+
+build: linux32
+ bash ./buildall.sh
+
+.PHONY: build
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+.PHONY: default depend build rmchroot
+CHRPTH = $(BUILD)/$(RES)
+TMPE2 = $(CHRPTH)/tmp/e2
+SCRIPT = build-driver buildrc build-script
+INIT = $(shell ls $(PROJ)/proj/init/)
+
+default:
+ echo "please use makefile at ./proj with target $(RES)"
+
+depend: $(TMPE2)/.e2deps
+
+$(TMPE2)/.e2deps: $(TMPE2)/.e2dept
+ while read r l ; do for d in $$r $$l ; do \
+ echo $$d $$r ; done ; done <$< | tsort >$@
+
+$(patsubst %,$(BUILD)/%/tmp/e2/.e2dept,$(DEPEND)): \
+ $(BUILD)/%/tmp/e2/.e2dept: $(PROJ)/res/%/makefile \
+ $(PROJ)/proj/makefile $(PROJ)/proj/makefile.res
+ make -C $(PROJ)/proj PROJ=$(PROJ) BUILD=$(BUILD) \
+ $(patsubst $(BUILD)/%/tmp/e2/.e2dept,depend-%,$@)
+
+$(TMPE2)/.e2dept: $(PROJ)/res/$(RES)/makefile \
+ $(PROJ)/proj/makefile $(PROJ)/proj/makefile.res \
+ $(patsubst %,$(BUILD)/%/tmp/e2/.e2dept,$(DEPEND))
+ mkdir -p $(TMPE2)
+ echo "$(RES) $(DEPEND)" | \
+ cat $(patsubst %,$(BUILD)/%/tmp/e2/.e2dept,$(DEPEND)) - >$@
+
+build: $(addprefix $(OUT)/$(RES)/,$(FILES))
+
+# copy result files from chroot to outdir:
+$(addprefix $(OUT)/$(RES)/,$(FILES)): $(OUT)/$(RES)/%: $(TMPE2)/out/%
+ mkdir -p $(OUT)/$(RES)
+ cp -dp $< $@
+ touch $(OUT)/$(RES)
+
+# clean up chroot to avoid collisions upon rebuild, let /tmp/e2 persist:
+rmchroot:
+ T=$$(mktemp -d $(CHRPTH).XXXXXX) && \
+ mv $(CHRPTH) $$T && \
+ mkdir -p $(CHRPTH)/tmp && \
+ mv $$T/$(RES)/tmp/e2 $(CHRPTH)/tmp/ && \
+ { rm -rf $$T & }
+
+# place chroot, exec build-script, cleanup chroot where KEEP is not set
+$(addprefix $(TMPE2)/out/,$(FILES)): $(TMPE2)/out/%: \
+ $(BUILD)/.bin/linux32 \
+ $(addprefix $(OUT)/,$(DEPEND)) \
+ $(addprefix $(TMPE2)/build/,$(SOURCE)) \
+ $(addprefix $(TMPE2)/script/,$(SCRIPT)) \
+ $(addprefix $(TMPE2)/init/,$(INIT))
+ make BUILD=$(BUILD) RES=$(RES) rmchroot
+ rm -rf $(TMPE2)/dep $(TMPE2)/out $(TMPE2)/root
+ mkdir -p $(TMPE2)/build $(TMPE2)/dep $(TMPE2)/out $(TMPE2)/root
+ for g in $(CHROOT) ; do \
+ for f in $(PROJ)/chroot/$$g/* ; do \
+ tar xzf $$f -C $(CHRPTH) 2>/dev/null || \
+ tar xjf $$f -C $(CHRPTH) 2>/dev/null || \
+ tar xf $$f -C $(CHRPTH); \
+ done ; \
+ done
+ test -z "$(DEPEND)" || \
+ cp -a $(addprefix $(OUT)/,$(DEPEND)) $(TMPE2)/dep/
+ test -z "$(CHROOT)" || test -z "$(KEEP)" || touch $(CHRPTH)/tmp/.chroot
+ test -n "$(CHROOT)" || \
+ env -i USER=$$USER PATH=/usr/sbin:/sbin:$$PATH T=$(TMPE2) r="$(RES)" \
+ $(BUILD)/.bin/linux32 \
+ /bin/bash -e -x $(TMPE2)/script/build-driver
+ test -z "$(CHROOT)" || \
+ env -i USER=$$USER PATH=/usr/sbin:/sbin:$$PATH T=/tmp/e2 r="$(RES)" \
+ $(BUILD)/.bin/linux32 chroot $(CHRPTH) \
+ /bin/bash -e -x /tmp/e2/script/build-driver
+ rm -rf $(TMPE2)/dep
+ # possibly remove the chroot except /tmp/e2:
+ test -f $(CHRPTH)/tmp/.chroot || \
+ make BUILD=$(BUILD) RES=$(RES) rmchroot
+ touch -c $(addprefix $(TMPE2)/out/,$(FILES))
+
+# provide pretendency tool:
+$(BUILD)/.bin/linux32: $(PROJ)/proj/linux32.c
+ mkdir -p $(BUILD)/.bin
+ cc -o $@ $<
+
+# provide source:
+$(addprefix $(TMPE2)/build/,$(SOURCE)):
+ mkdir -p $(TMPE2)/build
+ make -C $(PROJ)/src/$(patsubst $(TMPE2)/build/%,%,$@) \
+ PROJ=$(PROJ) BUILD=$(TMPE2)/build
+
+# retrieve build scripts:
+$(addprefix $(TMPE2)/script/,$(SCRIPT)):
+ mkdir -p $(TMPE2)/script
+ cp -dp $(PROJ)/.e2/bin/buildrc $(PROJ)/.e2/bin/build-driver \
+ $(PROJ)/res/$(RES)/build-script $(TMPE2)/script/
+
+# retrieve init scripts:
+$(addprefix $(TMPE2)/init/,$(INIT)): $(TMPE2)/%: $(PROJ)/proj/%
+ mkdir -p $(TMPE2)/init
+ cp -dp $< $@
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+CWD = $(shell pwd)
+PROJ = $(CWD)/..
+BUILD = $(PROJ)/build
+OUT = $(PROJ)/out
+DEFAULT = $(shell cat $(PROJ)/proj/default-results)
+ALL = $(shell ls $(PROJ)/res)
+
+.PHONY: default-results depend $(ALL) \
+ $(addprefix depend-,$(ALL)) $(addprefix build-,$(ALL))
+
+default-results: $(DEFAULT)
+
+$(ALL): %: depend-% build-%
+
+depend: $(addprefix depend-,$(ALL))
+
+$(addprefix depend-,$(ALL)):
+ make -C ../res/$(patsubst depend-%,%,$@) PROJ=$(PROJ) \
+ BUILD=$(BUILD) RES=$(patsubst depend-%,%,$@) depend
+
+$(addprefix build-,$(ALL)):
+ while read r ; do \
+ make -C ../res/$(patsubst build-%,%,$$r) PROJ=$(PROJ) \
+ BUILD=$(BUILD) OUT=$(OUT) RES=$(patsubst build-%,%,$$r) \
+ build || exit 1 ; \
+ done <$(BUILD)/$(patsubst build-%,%,$@)/tmp/e2/.e2deps
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- e2-new-source - add new source onto an existing server -*- Lua -*-
+
+
+require("e2local")
+e2lib.init()
+
+e2option.documentation = [[
+usage: e2-new-source --git [--server <server>] <name>
+ e2-new-source --files [--no-checksum]
+ [<server>:]<location> <source_file> [<checksum_file>]
+
+ Put new source onto an existing server.
+
+ --git
+ Put a repository named <name> into the projects' 'git/' directory on
+ the server, i.e. <server>/<project>/git/<name>.git
+ The server defaults to the default repository server, and the <project>
+ part is the project location relative to the projects server.
+
+ --files
+ Put a new file onto the server.
+ Server defaults to 'upstream'
+]]
+
+e2option.flag("git", "create a git repository")
+e2option.flag("files", "create a new file on a files server")
+e2option.option("server", "specify server")
+e2option.flag("no-checksum", "don't verify checksum")
+local opts = e2option.parse(arg)
+
+-- read a checksum from a file
+-- @param checksum_file string: the file containing the checksums
+-- @param filename string: the filename
+-- @return a table with fields checksum and checksum_type ("sha1", "md5")
+-- @return nil, or an error string on error
+function read_checksum(checksum_file, filename)
+ e2lib.log(4, string.format("read_checksum(%s, %s)", checksum_file,
+ filename))
+ local f, e = io.open(checksum_file, "r")
+ if not f then
+ return nil, e
+ end
+ local rc = nil
+ local e = new_error("no checksum available")
+ while true do
+ local line = f:read()
+ if not line then
+ break
+ end
+ local c, f = line:match("(%S+) (%S+)")
+ if (not c) or (not f) then
+ e:append("Checksum file has wrong format. ")
+ e:append("The standard sha1sum or md5sum format is "..
+ "required.")
+ return nil, e
+ end
+ if c and f and f == filename then
+ local cs = {}
+ cs.checksum = c
+ if c:len() == 40 then
+ cs.checksum_type = "sha1"
+ elseif c:len() == 32 then
+ cs.checksum_type = "md5"
+ else
+ rc = nil
+ e = "can't guess checksum type"
+ break
+ end
+ rc = cs
+ e = nil
+ break
+ end
+ end
+ f:close()
+ return rc, e
+end
+
+--- generate a sha1 checksum file
+-- @param source_file string: source file name
+-- @param checksum_file: checksum file name
+-- @return bool
+-- @return nil, an error string on error
+function write_checksum_file_sha1(source_file, checksum_file)
+ e2lib.log(4, string.format("write_checksum_file_sha1(%s, %s)",
+ source_file, checksum_file))
+ local cmd = string.format("sha1sum \"%s\" >\"%s\"", source_file,
+ checksum_file)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, "error writing checksum file"
+ end
+ return true, nil
+end
+
+function download(f)
+ local name = e2lib.basename(f)
+ local cmd = string.format("curl --silent --fail \"%s\" >\"%s\"", f,
+ name)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, "download failed"
+ end
+ return true, nil
+end
+
+function mv(s, d)
+ local cmd = string.format("mv \"%s\" \"%s\"", s, d)
+ local rc = e2lib.callcmd_capture(cmd)
+ if rc ~= 0 then
+ return false, "mv failed"
+ end
+ return true, nil
+end
+
+--- new files source
+-- @param location string: server/location string
+-- @param source_file string: source file url
+-- @param checksum_file string: checksum file url
+-- @param flags table: flags
+-- @return bool
+-- @return nil, an error string on error
+function new_files_source(c, server, location, source_file, checksum_file,
+ checksum_file_format, no_checksum)
+ local source_file_base = e2lib.basename(source_file)
+ local do_checksum = (not no_checksum)
+ local checksum_type = "sha1"
+ local checksum_file_base
+ local checksum_file1
+ local checksum_file2 = string.format("%s.%s", source_file_base,
+ checksum_type)
+ local cs1, cs2
+ local rc, e
+ if not do_checksum then
+ e2lib.warn("WOTHER", "Checksum verifying is disabled!")
+ end
+
+ -- change to a temporary directory
+ local tmpdir, e = e2lib.mktempdir()
+ if not e2lib.chdir(tmpdir) then
+ e2lib.abort("can't chdir")
+ end
+
+ -- download
+ e2lib.log(1, string.format("fetching %s ...", source_file))
+ local rc, e = download(source_file)
+ if not rc then
+ e2lib.abort(estring(e, source_file))
+ end
+
+ -- checksum checking
+ if do_checksum then
+ e2lib.log(1, string.format("fetching %s ...", checksum_file))
+ rc, e = download(checksum_file)
+ if not rc then
+ e2lib.abort(estring(e, checksum_file))
+ end
+ checksum_file_base = e2lib.basename(checksum_file)
+ checksum_file1 = string.format("%s.orig",
+ checksum_file_base)
+ rc = mv(checksum_file_base, checksum_file1)
+ if not rc then
+ e2lib.abort(e)
+ end
+ cs1, e = read_checksum(checksum_file1, source_file_base)
+ if not cs1 then
+ e2lib.abort(e)
+ end
+ checksum_type = cs1.checksum_type
+ end
+
+ -- write the checksum file to store on the server
+ rc = write_checksum_file_sha1(source_file_base, checksum_file2)
+ cs2, e = read_checksum(checksum_file2, source_file_base)
+ if not cs2 then
+ e2lib.abort(e)
+ end
+
+ -- compare checksums
+ if do_checksum then
+ if cs1.checksum == cs2.checksum then
+ e2lib.log(2, string.format(
+ "checksum matches (%s): %s",
+ cs1.checksum_type, cs1.checksum))
+ else
+ e2lib.abort("checksum mismatch")
+ end
+ end
+
+ -- store
+ local flags = {}
+ local rlocation = string.format("%s/%s", location, source_file_base)
+ e2lib.log(1, string.format("storing file %s to %s:%s",
+ source_file_base, server, rlocation))
+ local rc, e = cache.push_file(c, source_file_base, server,
+ rlocation, flags)
+ if not rc then
+ e2lib.abort(e)
+ end
+ local rlocation = string.format("%s/%s", location, checksum_file2)
+ e2lib.log(1, string.format("storing file %s to %s:%s",
+ checksum_file2, server, rlocation))
+ local rc, e = cache.push_file(c, checksum_file2, server,
+ rlocation, flags)
+ if not rc then
+ e2lib.abort(e)
+ end
+ if not e2lib.chdir("/") then
+ e2lib.abort("can't chdir")
+ end
+ return true, nil
+end
+
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-new-source")
+
+if opts.git then
+ if #opts.arguments ~= 1 then
+ e2lib.abort("<name> argument required")
+ end
+ -- remote
+ local rserver = info.default_repo_server
+ if opts["server"] then
+ rserver = opts["server"]
+ end
+ local name = opts.arguments[1]
+ local rlocation = string.format("%s/git/%s.git", info.project_location, name)
+ -- local
+ local lserver = info.root_server_name
+ local llocation = string.format("in/%s/.git", name)
+ local flags = {}
+ local rc, re = e2scm["git"].new_git_source(info.cache, lserver, llocation,
+ rserver, rlocation, flags)
+ if not rc then
+ e2lib.abort(re)
+ end
+ e2lib.log(1,
+ "See e2-new-source(1) or call e2-new-source --help to see how to go on")
+elseif opts.files then
+ local location = opts.arguments[1]
+ local sl, e = e2lib.parse_server_location(location, info.default_files_server)
+ if not sl then
+ e2lib.abort(e)
+ end
+ local server = sl.server
+ local location = sl.location
+ local source_file = opts.arguments[2]
+ local checksum_file = opts.arguments[3]
+ local checksum_file_format = opts["checksum-file"]
+ local no_checksum = opts["no-checksum"]
+ if not no_checksum and not checksum_file then
+ e2lib.abort("checksum file not given")
+ end
+ local rc = new_files_source(info.cache, server, location, source_file,
+ checksum_file, checksum_file_format, no_checksum)
+else
+ e2lib.log(1,
+ "Creating repositories other than git is not supported yet.\n"..
+ "You might nevertheless use 'e2-use-source' to use existing\n"..
+ "sources in your project")
+end
+
+e2hook.run_hook(info, "tool-finish", nil, "e2-new-source")
+e2lib.finish(0)
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- playground - enter existing chroot(1) environment -*- Lua -*-
+
+require("e2local")
+e2lib.init()
+
+local e = new_error("entering playground failed")
+local rc, re
+
+e2option.documentation = [[
+usage: e2-playground RESULT
+
+Jump into chroot(1) environment for specified result, if it exists.
+]]
+e2option.option("command","execute command in chroot")
+e2option.flag("runinit","run init files automatically")
+e2option.flag("showpath", "prints the path of the build directory inside the chroot to stdout" )
+
+local opts = e2option.parse(arg)
+local info, re = e2tool.collect_project_info()
+if not info then
+ e2lib.abort(re)
+end
+local rc, re = e2tool.check_project_info(info)
+if not rc then
+ e2lib.abort(re)
+end
+
+e2lib.log_invocation(info, arg)
+e2hook.run_hook(info, "tool-start", nil, "e2-playground")
+
+if #opts.arguments ~= 1 then
+ e2option.usage(1)
+end
+
+r = opts.arguments[1]
+
+--e2hook.run_hook(info, "enter-playground", c.c "e2-playground")
+-- get build mode from the command line
+local build_mode = policy.handle_commandline_options(opts, true)
+if not build_mode then
+ e2lib.abort("no build mode given")
+end
+-- apply the standard build mode to all results
+for _,res in pairs(info.results) do
+ res.build_mode = build_mode
+end
+rc, re = e2build.build_config(info, r, {})
+if not rc then
+ e2lib.abort(e:cat(re))
+end
+if opts.showpath then
+ print(info.results[r].build_config.c)
+ e2lib.finish(0)
+end
+-- interactive mode, use bash profile
+local res = info.results[r]
+local bc = res.build_config
+local profile = string.format("%s/%s", bc.c, bc.profile)
+local f, msg = io.open(profile, "w")
+if not f then
+ e2lib.abort(e:cat(msg))
+end
+f:write(string.format("export TERM='%s'\n", e2lib.osenv["TERM"]))
+f:write(string.format("export HOME=/root\n"))
+if opts.runinit then
+ f:write(string.format("source %s/script/%s\n", bc.Tc, bc.buildrc_file))
+else
+ f:write(string.format("function runinit() { source %s/script/%s; }\n",
+ bc.Tc, bc.buildrc_file))
+ f:write(string.format("source %s/script/%s\n", bc.Tc,
+ bc.buildrc_noinit_file))
+end
+f:close()
+local command = nil
+if opts.command then
+ command = string.format("/bin/bash --rcfile '%s' -c '%s'", bc.profile,
+ opts.command)
+else
+ command = string.format("/bin/bash --rcfile '%s'", bc.profile)
+end
+e2lib.logf(2, "entering playground for %s", r)
+if not opts.runinit then
+ e2lib.log(2, "type `runinit' to run the init files")
+end
+rc, re = e2build.enter_playground(info, r, command)
+if not rc then
+ e2lib.abort(re)
+end
+--e2hook.run_hook(info, "tool-finish", nil, "e2-playground")
+e2lib.finish()
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+e2policy = e2lib.module("e2policy")
+
+local function source_set_lazytag()
+ return "lazytag"
+end
+local function source_set_tag()
+ return "tag"
+end
+local function source_set_branch()
+ return "branch"
+end
+local function source_set_working_copy()
+ return "working-copy"
+end
+
+
+local results_server = "results"
+local release_server = "releases"
+local local_server = "."
+local function storage_release(location, release_id)
+ return release_server, string.format("%s/release/%s", location,
+ release_id)
+end
+local function storage_default(location, release_id)
+ return results_server, string.format("%s/shared", location)
+end
+local function storage_local(location, release_id)
+ return local_server, string.format("out")
+end
+
+
+local function dep_set_buildid(buildid)
+ return buildid
+end
+local function dep_set_last(buildid)
+ return "last"
+end
+
+
+local function buildid_buildid(buildid)
+ return buildid
+end
+local function buildid_scratch(buildid)
+ return "scratch"
+end
+
+--- set a policy mode to a value
+-- @class function
+-- @name policy.set
+-- @param id string: the policy identifier: storage, source_set, dep_set,
+-- buildid
+-- @param val the function to use : storage_*, source_set_*, etc.
+-- @return nil
+local function set(mode, id, val)
+ if not id or not val then
+ print(id)
+ print(val)
+ e2lib.abort("trying to set nil value in policy.set()")
+ end
+ mode[id] = val
+ return nil
+end
+
+--- get a policy function
+-- @class function
+-- @name policy.get
+-- @param id string: the policy identifier: storage, source_set, dep_set,
+-- buildid
+-- @return function: the policy function
+local function get(mode, id)
+ if type(mode) ~= "table" then
+ print(mode, id)
+ e2lib.abort("policy.get() mode is not a table")
+ end
+ return mode[id]
+end
+
+--- source_set_* get the source set identifier
+-- @class function
+-- @name policy.source_set_*
+-- @param none
+-- @return string: the source set identifier
+
+--- storage_*
+-- @class function
+-- @name policy.storage_*
+-- @param location string: the project location
+-- @param release_id string: the release id
+-- @return the server to store the result on
+-- @return the location to store the result in
+
+--- dep_set_*
+-- @class function
+-- @name policy.dep_set_*
+-- @param buildid the buildid
+-- @return the buildid
+
+--- buildid_* get the buildid for a build
+-- @class function
+-- @name policy.buildid_*
+-- @param buildid the buildid
+-- @return the buildid
+
+function init(info)
+ local e = new_error("checking policy")
+ -- check if all required servers exist
+ local storage = {
+ storage_release,
+ storage_default,
+ storage_local,
+ }
+ for i,s in ipairs(storage) do
+ local location = "test/test"
+ local release_id = "release-id"
+ local server, location = s(location, release_id)
+ local se = new_error("checking server configuration for '%s'",
+ server)
+ local ce, re = ce_by_server(info.cache, server)
+ if not ce then
+ se:cat(re)
+ elseif not ce.flags.writeback then
+ e2lib.warnf("WPOLICY",
+ "Results will not be pushed to server: '%s'"..
+ " (Writeback disabled)", server)
+ end
+ if ce and not (ce.flags.cache or ce.flags.islocal) then
+ se:append(
+ "Building needs local access to build results. "..
+ "Enable cache.")
+ elseif ce and not (ce.flags.writeback or ce.flags.cache) then
+ se:append(
+ "Cannot store results. "..
+ "Enable cache or writeback.")
+ end
+ if se:getcount() > 1 then
+ e:cat(se)
+ end
+ end
+ if e:getcount() > 1 then
+ return false, e
+ end
+ return true, nil
+end
+
+function register_commandline_options()
+ e2option.option("build-mode", "set build mode to calculate buildids")
+ e2option.flag("tag", "set build mode to 'tag' (default)")
+ e2option.flag("branch", "set build mode to 'branch'")
+ e2option.flag("working-copy", "set build mode to 'working-copy'")
+ e2option.flag("release", "set build mode to 'release'")
+end
+
+function handle_commandline_options(opts, use_default)
+ local nmodes = 0
+ local mode = nil
+ if opts["build-mode"] then
+ nmodes = nmodes + 1
+ end
+ if opts["tag"] then
+ opts["build-mode"] = "tag"
+ nmodes = nmodes + 1
+ end
+ if opts["release"] then
+ opts["build-mode"] = "release"
+ nmodes = nmodes + 1
+ end
+ if opts["branch"] then
+ opts["build-mode"] = "branch"
+ nmodes = nmodes + 1
+ end
+ if opts["working-copy"] then
+ opts["build-mode"] = "working-copy"
+ nmodes = nmodes + 1
+ end
+ if nmodes > 1 then
+ e2lib.abort("Error: Multiple build modes are not supported")
+ end
+ if not opts["build-mode"] and use_default then
+ e2lib.warn("WDEFAULT", string.format(
+ "build-mode defaults to '%s'",
+ policy.default_build_mode_name))
+ opts["build-mode"] = policy.default_build_mode_name
+ end
+ if opts["build-mode"] then
+ if policy.default_build_mode[opts["build-mode"]] then
+ mode = policy.default_build_mode[opts["build-mode"]]
+ else
+ e2lib.abort("invalid build mode")
+ end
+ end
+ return mode
+end
+
+local default_build_process = {
+ "build_config",
+ "result_available",
+ "remove_logfile",
+ "chroot_cleanup_if_exists",
+ "setup_chroot",
+ -- XXX run build-setup-chroot hook here
+ "sources",
+ "collect_project",
+ -- XXX run build-pre-runbuild hook here
+ -- XXX to be done: e2build.playground: stop if playgrund requested
+ "fix_permissions",
+ "playground",
+ "runbuild",
+ -- XXX how to handle the build failure hook?
+ -- XXX run build-post-runbuild hook here
+ "store_result",
+ "linklast",
+ "chroot_cleanup",
+ -- XXX run build-remove-chroot hook here?
+}
+
+policy = {}
+policy.init = init
+policy.register_commandline_options = register_commandline_options
+policy.default_build_mode_name = "tag"
+policy.handle_commandline_options = handle_commandline_options
+policy.set = set
+policy.get = get
+policy.source_set_lazytag = source_set_lazytag
+policy.source_set_tag = source_set_tag
+policy.source_set_branch = source_set_branch
+policy.source_set_working_copy = source_set_working_copy
+policy.storage_release = storage_release
+policy.storage_default = storage_default
+policy.storage_local = storage_local
+policy.dep_set_buildid = dep_set_buildid
+policy.dep_set_last = dep_set_last
+policy.buildid_buildid = buildid_buildid
+policy.buildid_scratch = buildid_scratch
+
+policy.default_build_mode = {}
+policy.default_build_mode["lazytag"] = {
+ source_set = policy.source_set_lazytag,
+ dep_set = policy.dep_set_buildid,
+ buildid = policy.buildid_buildid,
+ storage = policy.storage_default,
+ build_process = default_build_process
+}
+
+policy.default_build_mode["tag"] = {
+ source_set = policy.source_set_tag,
+ dep_set = policy.dep_set_buildid,
+ buildid = policy.buildid_buildid,
+ storage = policy.storage_default,
+ build_process = default_build_process
+}
+
+policy.default_build_mode["release"] = {
+ source_set = policy.source_set_tag,
+ dep_set = policy.dep_set_buildid,
+ buildid = policy.buildid_buildid,
+ storage = policy.storage_release,
+ build_process = default_build_process
+}
+
+policy.default_build_mode["branch"] = {
+ source_set = policy.source_set_branch,
+ dep_set = policy.dep_set_buildid,
+ buildid = policy.buildid_buildid,
+ storage = policy.storage_default,
+ build_process = default_build_process
+}
+
+policy.default_build_mode["working-copy"] = {
+ source_set = policy.source_set_working_copy,
+ dep_set = policy.dep_set_last,
+ buildid = policy.buildid_scratch,
+ storage = policy.storage_local,
+ build_process = default_build_process
+}
+
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- result.lua -*- Lua -*-
+
+-- Documentation: stored result filesystem format version 1:
+-- path/metadata file containing an integer version number
+
+-- Required metadata
+-- - version, just to be extensible
+-- - timestamp, to find old results
+-- - human readable timestamp, for convenience
+-- - path to the result files, relative to the metadata file
+-- - list of files, to allow copying a result via transports that don't
+-- understand recursion (e.g. http)
+--
+-- Metadata ist stored in a file named "metadata"
+--
+-- Format of the metadata file
+-- <version>
+-- <timestamp>
+-- <timestamp_human>
+-- <files path>
+-- <space><f1>
+-- <space>
+-- <space><fn>
+--
+-- Fetching a stored result:
+-- 1. fetch the metadata
+-- 2. read the list of files
+-- 3. fetch the files
+--
+-- Pushing a stored result:
+-- 1. create the remote directory
+-- 2. push the metadata
+-- 3. push the files
+
+-- Stored Result data structure
+-- General:
+-- sr.metadata_file file that holds the version number
+-- Version 1:
+-- sr.version integer version number (1)
+-- sr.files_path location of the result files in the filesystem
+-- sr.files table of files that make up the result
+
+local result_autoconvert = true
+
+--- create a new stored result data structure
+-- @return the stored result data structure
+-- @return nil and an error string on error
+function new()
+ --- stored result data structure
+ -- @class table
+ -- @name sr
+ -- @field version version number
+ -- @field metadata_file name of the metadata file
+ -- @field files_path path to the result files relative to the meta
+ -- data file
+ -- @field files table of result file tables, to be included in the
+ -- metadata file for stupid transports
+ -- @field new new()
+ -- @field read read()
+ -- @field get_files get_files()
+ -- @field update_timestamp update_timestamp()
+ local sr = {}
+
+ -- variables
+ sr.version = 1
+ sr.metadata_file = "metadata"
+ sr.files_path = "files"
+ sr.files = {}
+
+ -- functions
+ sr.new = new
+ sr.store = store
+ sr.fetch = fetch
+ sr.fetchfiles = fetchfiles
+ sr.read = read
+ sr.write = write
+ sr.get_files = get_files
+ sr.get_filelist = get_filelist
+ sr.add_file = add_file
+ sr.update_timestamp = update_timestamp
+
+ sr:update_timestamp()
+ return sr
+end
+
+--- update the timestamp
+-- @return void this function always succeeds
+function update_timestamp(sr)
+ sr.timestamp = os.date("%s")
+ sr.timestamp_human = os.date("%c", sr.timestamp)
+end
+
+--- convert results from the old format (plain directory with files in it)
+-- to version 1 (with metadata file)
+-- @param path path to the result
+-- @return the stored result on success, or nil on error
+-- @return nil, an error string on error
+function convert0_1(path)
+ local sr, e
+ sr = result.new()
+ local f = io.open(path .. "/" .. sr.metadata_file, "r")
+ if f then
+ f:close()
+ e2lib.abort("can't convert result: unknown version: " .. path)
+ end
+ local test = string.format("test -d %s/", path)
+ if e2lib.callcmd_capture(test) ~= 0 then
+ return false, "result does not exist: " .. path
+ end
+ e2lib.log(1, "converting result: " .. path)
+ local ls = string.format("ls %s", path)
+ local lsp = io.popen(ls, "r")
+ if not lsp then
+ e2lib.abort("can't list result files: " .. ls)
+ end
+ while true do
+ local l = lsp:read()
+ if not l then
+ break
+ end
+ local file = {}
+ file.name = l
+ file.sha1 = nil
+ table.insert(sr.files, file)
+ end
+ lsp:close()
+ if #sr.files == 0 then
+ return false, "result with no files, treating as absent."
+ end
+ local mkdir, mv
+ mkdir = string.format("mkdir %s/%s", path, sr.files_path)
+ if e2lib.callcmd_capture(mkdir) ~= 0 then
+ return false, "result conversion failed: " .. mkdir
+ end
+ for _, file in pairs(sr.files) do
+ mv = string.format("mv %s/%s %s/%s/%s",
+ path, file.name, path, sr.files_path, file.name)
+ if e2lib.callcmd_capture(mv) ~= 0 then
+ return false, "result conversion failed: " .. mv
+ end
+ end
+ sr:update_timestamp()
+ sr:write(path)
+ return true
+end
+
+--- read metadata
+-- @param path
+-- @return the stored result description table, nil on error
+-- @return nil, an error string on error
+function read(path)
+ e2lib.log(4, string.format("result.read(): %s", tostring(path)))
+ local sr = result.new()
+ if not (type(path) == "string") then
+ return nil, "wrong argument type: path"
+ end
+ local f = io.open(path .. "/" .. sr.metadata_file, "r")
+ if not f and result_autoconvert then
+ local r, e = result.convert0_1(path)
+ -- don't care if this succeeded. The next open will succeed
+ -- or not and error checking is in place.
+ f = io.open(path .. "/" .. sr.metadata_file, "r")
+ end
+ if not f then
+ return nil, "can't open version file: " .. sr.metadata_file
+ end
+ local version = f:read()
+ if not version or
+ not type(version) == "string" or not version:match("(%d+)") then
+ return nil, "can't parse metadata file: " .. sr.metadata_file
+ end
+ sr.version = tonumber(version)
+ if sr.version == 1 then
+ sr.timestamp = tonumber(f:read())
+ sr.timestamp_human = f:read()
+ sr.files_path = f:read()
+ while true do
+ local line, fname
+ line = f:read()
+ if not line then
+ break
+ end
+ fname = line:match("%s(.+)")
+ if not fname then
+ break
+ end
+ local file = {}
+ file.name = fname
+ file.sha1 = nil
+ table.insert(sr.files, file)
+ end
+ else
+ return nil, "unknown stored result version:" .. sr.version
+ end
+ f:close()
+ if not ( sr.timestamp >= 1 ) or
+ not ( type(sr.timestamp_human) == "string" ) or
+ not ( type(sr.files_path) == "string" ) then
+ return nil, "can't parse result metadata file: " ..
+ sr.metadata_file
+ end
+ return sr, nil
+end
+
+--- write metadata file
+-- @param sr stored result data structure
+-- @param path filesystem path to the stored result
+-- @return the sr structure on success, nil
+-- @return an error string on error, nil otherwise
+function write_metadata1(sr, path)
+ local f, success, rc
+ f = io.open(path .. "/" .. sr.metadata_file, "w")
+ if not f then
+ return nil, "can't open metadata file: " .. sr.metadata_file
+ end
+ local metadata = string.format(
+ "%d\n" .. -- version
+ "%d\n" .. -- timestamp
+ "%s\n" .. -- timestamp_human
+ "%s\n", -- files_path
+ sr.version, sr.timestamp, sr.timestamp_human, sr.files_path)
+ success = f:write(metadata)
+ if not success then
+ return nil,
+ "can't write to metadata file: " .. sr.metadata_file
+ end
+ for _,fn in pairs(sr.files) do
+ if not f:write(string.format(" %s\n", fn.name)) then
+ return nil, "can't write to metadata file: " ..
+ sr.metadata_file
+ end
+ end
+ f:close()
+ return sr, nil
+end
+
+--- write metadata
+-- @param sr stored result data structure
+-- @param path path to the stored result in the filesystem
+-- @param create bool newly create the structure in the filesystem?
+-- @return the sr structure on success
+-- @return nil and an error string on failure
+function write(sr, path, create)
+ if not sr or not path then
+ return nil, "missing argument"
+ end
+ if sr.version == 1 then
+ local rc, e
+ rc, e = write_metadata1(sr, path)
+ if not rc then
+ return nil, e
+ end
+ local dir = string.format("%s/%s", path, sr.files_path)
+ if create then
+ if os.execute("mkdir " .. dir) ~= 0 then
+ return nil, "can't create files directory: "
+ .. dir
+ end
+ end
+ return sr, nil
+ end
+ return nil, "unknown stored result version:" .. sr.version
+end
+
+--- get the directory holding the result files for a stored result
+-- @param sr stored result data structure
+-- @return the location of the result files inside the result structure
+-- @return nil and an error string on failure
+function get_files(sr)
+ if not sr then
+ return nil, "missing argument"
+ end
+ if sr.version == 1 then
+ return sr.files_path, nil
+ end
+ return nil, "unknown stored result version:" .. sr.version
+end
+
+--- result file table, holds file names relativ to the path to the result,
+-- e.g.
+-- { name="metadata", sha1="abcdef..." } or
+-- { sourcefile="/tmp/file1.tar.gz", name="files/file1.tar.gz",
+-- sha1="abc123..." }
+-- @class table
+-- @name file
+-- @field sourcefile path to sourcefile
+-- @field name filename
+-- @field sha1 sha1 checksum, may be nil
+
+--- get_filelist_flags
+-- @class table
+-- @name get_filelist_flags
+-- @field all include all files
+-- @field metadata inlude metadata files
+-- @field result_files include result files
+
+--- return a table of files that make up the result.
+-- @param sr stored result table
+-- @param flags table of flags (all, metadata, result_files)
+-- @return table of file tables, or nil
+-- @return nil, or error string
+function get_filelist(sr, flags)
+ if not sr then
+ return nil, "missing argument"
+ end
+ flags.metadata = flags.metadata or flags.all
+ flags.result_files = flags.result_files or flags.all
+ if sr.version == 1 then
+ local t = {}
+ for _,f in pairs(sr.files) do
+ local file = {}
+ file.sourcefile = f.sourcefile
+ file.name = string.format("%s/%s",
+ sr.files_path, f.name)
+ file.sha1 = f.sha1
+ if flags.result_files then
+ table.insert(t, file)
+ end
+ end
+ local file = {}
+ file.name = sr.metadata_file
+ file.sha1 = nil
+ file.sourcefile = nil
+ if flags.metadata then
+ table.insert(t, file)
+ end
+ return t, nil
+ end
+ return nil, "unknown stored result version:" .. sr.version
+end
+
+--- add a new result file to the stored result table
+-- @param sr stored result table
+-- @param path string
+-- @param class file class (optional, for internal use)
+-- @return bool
+-- @return nil, or an error string
+function add_file(sr, path, class)
+ if (not sr) or (not path) then
+ return nil, "missing argument"
+ end
+ if not class then
+ class = "result"
+ end
+ local file = {}
+ file.class = class
+ file.sourcefile = path
+ file.name = e2lib.basename(path)
+ file.sha1 = nil --XXX ignoring sha
+ table.insert(sr.files, file)
+ return true
+end
+
+--- store a result to a server
+-- @param sr stored result table
+-- @param c table: cache
+-- @param server string: server name
+-- @param baselocation string: location
+-- @param tocache bool: store to cache
+-- @param toserver bool: store to server
+-- @return bool
+-- @return an error object on failure
+function store(sr, c, server, baselocation, tocache, toserver)
+ local rc, re
+ local e = new_error("storing result failed")
+ -- write metadata to a temporary directory
+ local tmp = e2lib.mktempdir()
+ rc, re = sr:write(tmp)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- set cache flags
+ local cache_flags = {}
+ -- push metadata to its locations
+ local flags = { metadata=true }
+ for _,f in pairs(sr:get_filelist(flags)) do
+ local sfile = string.format("%s/%s", tmp, f.name)
+ local dlocation = string.format("%s/%s", baselocation, f.name)
+ rc, re = cache.push_file(c, sfile, server, dlocation,
+ cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ e2lib.rmtempdir(tmp)
+ -- push result files to their locations
+ local flags = { result_files=true }
+ for _,f in pairs(sr:get_filelist(flags)) do
+ local sfile = f.sourcefile
+ local dlocation = string.format("%s/%s", baselocation, f.name)
+ rc, re = cache.push_file(c, sfile, server, dlocation,
+ cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+end
+
+--- cache a result. Return true if the result is in the cache, or false
+-- if it is not, or the cache is disabled.
+-- @param sr stored result table
+-- @param c table: cache
+-- @param server string: server name
+-- @param baselocation string: location
+-- @return bool
+-- @return an error object on failure
+function fetch(c, server, baselocation)
+ local e = new_error("caching result failed")
+ local rc, re
+ local mp, rp, sr
+ local location1 = string.format("%s/metadata", baselocation)
+ local cache_flags
+ if not cache.cache_enabled(c, server) then
+ return false, new_error("cache disabled")
+ end
+ if cache.file_in_cache(c, server, location1) then
+ -- result is available
+ return true, nil
+ end
+ -- try to get the metadata file
+ cache_flags = {}
+ rc, re = cache.cache_file(c, server, location1, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ mp, re = cache.file_path(c, server, location1, cache_flags)
+ if not mp then
+ -- result is not available
+ e:append("result is not available")
+ return false, e:cat(re)
+ end
+ -- metadata file is ready
+ rp = e2lib.dirname(mp)
+ sr, re = result.read(rp)
+ if not sr then
+ return false, e:cat(re)
+ end
+ -- cache the result files
+ local flags = { result_files = true }
+ for _,file in pairs(sr:get_filelist(flags)) do
+ location1 = string.format("%s/%s", baselocation, file.name)
+ rc, re = cache.cache_file(c, server, location1, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ end
+ -- result was fetched. OK.
+ return true, nil
+end
+
+function available_local(c, server, baselocation)
+ local location = string.format("%s/metadata", baselocation)
+ return cache.file_local(c, server, location)
+end
+
+--- fetchfiles fetch result files to a directory
+-- @param c table: cache
+-- @param server string: server name
+-- @param baselocation string: location
+-- @return a sr object, nil on failure
+-- @return an error object on failure
+function fetchfiles(c, server, baselocation, destdir)
+ local cache_flags = { check_only=true, }
+ local e = new_error("result not available: %s:%s", server,
+ baselocation)
+ local rc, re = cache.file_path(c, server, baselocation, cache_flags)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local path, re = cache.file_path(c, server, baselocation, cache_flags)
+ if not path then
+ return false, e:cat(re)
+ end
+ local sr = result.read(path)
+ if not sr then
+ return false, e:cat(re)
+ end
+ local flags = { result_files = true }
+ for _,f in pairs(sr:get_filelist(flags)) do
+ local cache_flags = { chmod = "644", }
+ local location = string.format("%s/%s", baselocation, f.name)
+ rc, re = cache.fetch_file(c, server, location, destdir, nil,
+ cache_flags)
+ if not rc then
+ e = new_error("incomplete result")
+ return false, e:cat(re)
+ end
+ end
+ return true, nil
+end
+
+---function test()
+-- local sr
+-- sr, e = result.read("foo")
+-- if not sr then
+-- print(e)
+-- return nil
+-- end
+-- sr:write("bar", true)
+-- sr.timestamp = sr.timestamp + 30
+-- sr.files = { "f1", "f2", "f3" }
+-- sr:write("bar", false)
+--end
+
+-- stored result object
+result = {}
+
+-- functions
+result.convert0_1 = convert0_1
+result.new = new
+result.store = store
+result.fetch = fetch
+result.available_local = available_local
+result.fetchfiles = fetchfiles
+result.read = read
+result.write = write
+result.get_files = get_files
+result.add_file = add_file
+result.get_filelist = get_filelist
+result.update_timestamp = update_timestamp
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- svn.lua - Subversion-specific SCM operations -*- Lua -*-
+--
+-- See e2scm.lua for more information about these operations.
+--
+-- The code assumes the directory layout in a svn repository looks like this:
+--
+-- <source>/<branch>/<dir>
+-- <source>/<tag>/<dir>
+
+-- helper functions
+
+--- translate url into subversion url
+-- @param u table: url table
+-- @return string: subversion style url
+-- @return an error object on failure
+function mksvnurl(surl)
+ local rc, re
+ local e = new_error("cannot translate url into subversion url:")
+ e:append("%s", surl)
+ local u, re = url.parse(surl)
+ if not u then
+ return nil, e:cat(re)
+ end
+ local transport
+ if u.transport == "ssh" or u.transport == "rsync+ssh" then
+ transport = "svn+ssh"
+ elseif u.transport == "file" then
+ transport = "file"
+ else
+ return nil, e:append("invalid transport")
+ end
+ return string.format("%s://%s/%s", transport, u.server, u.path)
+end
+
+-- the scm interface
+
+local svn = {}
+
+function svn.fetch_source(info, sourcename) --OK
+ local rc, re = svn.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local e = new_error("fetching source failed: %s", sourcename)
+ local src = info.sources[ sourcename ]
+ local location = src.location
+ local server = src.server
+ local surl, re = cache.remote_url(info.cache, server, location)
+ if not surl then
+ return false, e:cat(re)
+ end
+ local svnurl, re = mksvnurl(surl)
+ if not svnurl then
+ return false, e:cat(re)
+ end
+ local args = string.format("checkout '%s' '%s/%s'", svnurl, info.root,
+ src.working)
+ rc, re = e2lib.svn(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true, nil
+end
+
+function svn.prepare_source(info, sourcename, source_set, build_path) --OK
+ local rc, re = svn.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local e = new_error("svn.prepare_source failed")
+ local src = info.sources[ sourcename ]
+ local location = src.location
+ local server = src.server
+ local surl, re = cache.remote_url(info.cache, server, location)
+ if not surl then
+ return false, e:cat(re)
+ end
+ local svnurl, re = mksvnurl(surl)
+ if not svnurl then
+ return false, e:cat(re)
+ end
+ if source_set == "tag" or source_set == "branch" then
+ local rev
+ if source_set == "tag" then
+ rev = src.tag
+ else -- source_set == "branch"
+ rev = src.branch
+ end
+ local args = string.format("export '%s/%s' '%s/%s'", svnurl, rev,
+ build_path, sourcename)
+ rc, re = e2lib.svn(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ elseif source_set == "working-copy" then
+ -- cp -R info.root/src.working/src.workingcopy_subdir build_path
+ local s = string.format("%s/%s/%s", info.root, src.working,
+ src.workingcopy_subdir)
+ local d = string.format("%s/%s", build_path, src.name)
+ rc, re = e2lib.cp(s, d, "-R")
+ if not rc then
+ return false, e:cat(re)
+ end
+ else
+ return false, e:cat("invalid source set")
+ end
+ return true, nil
+end
+
+function svn.working_copy_available(info, sourcename) --OK
+ local rc, re
+ rc, re = svn.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local src = info.sources[sourcename]
+ local dir = string.format("%s/%s", info.root, src.working)
+ return e2lib.isdir(dir)
+end
+
+function svn.check_workingcopy(info, sourcename) --OK
+ local rc, re
+ local e = new_error("checking working copy failed")
+ e:append("in source %s (svn configuration):", sourcename)
+ e:setcount(0)
+ rc, re = svn.validate_source(info, sourcename)
+ if not rc then
+ return false, re
+ end
+ local src = info.sources[sourcename]
+ if e:getcount() > 0 then
+ return false, e
+ end
+ -- check if the configured branch and tag exist
+ local d
+ d = string.format("%s/%s/%s", info.root, src.working, src.branch)
+ if not e2lib.isdir(d) then
+ e:append("branch does not exist: %s", src.branch)
+ end
+ d = string.format("%s/%s/%s", info.root, src.working, src.tag)
+ if not e2lib.isdir(d) then
+ e:append("tag does not exist: %s", src.tag)
+ end
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+function svn.has_working_copy(info, sname) --OK
+ return true
+end
+
+--- create a table of lines for display
+-- @param info the info structure
+-- @param sourcename string
+-- @return a table, nil on error
+-- @return an error string on failure
+function svn.display(info, sourcename) --OK
+ local src = info.sources[sourcename]
+ local rc, e
+ rc, e = svn.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ local display = {}
+ display[1] = string.format("type = %s", src.type)
+ display[2] = string.format("server = %s", src.server)
+ display[3] = string.format("remote = %s", src.location)
+ display[4] = string.format("branch = %s", src.branch)
+ display[5] = string.format("tag = %s", src.tag)
+ display[6] = string.format("working = %s", src.working)
+ local i = 7
+ for _,l in pairs(src.licences) do
+ display[i] = string.format("licence = %s", l)
+ i = i + 1
+ end
+ return display
+end
+
+--- calculate an id for a source
+-- @param info
+-- @param sourcename
+function svn.sourceid(info, sourcename, source_set) --OK
+ local src = info.sources[sourcename]
+ local rc, e
+ rc, e = svn.validate_source(info, sourcename)
+ if not rc then
+ return false, e
+ end
+ if not src.sourceid then
+ src.sourceid = {}
+ end
+ src.sourceid["working-copy"] = "working-copy"
+ if src.sourceid[source_set] then
+ return src.sourceid[source_set]
+ end
+ local hc = hash.hash_start()
+ hash.hash_line(hc, src.name)
+ hash.hash_line(hc, src.type)
+ for _,l in pairs(src.licences) do
+ hash.hash_line(hc, l)
+ end
+ -- svn specific
+ hash.hash_line(hc, src.branch)
+ hash.hash_line(hc, src.tag)
+ hash.hash_line(hc, src.server)
+ hash.hash_line(hc, src.location)
+ e2lib.log(4, string.format("hash data for source %s\n%s", src.name,
+ hc.data))
+ src.sourceid[source_set] = hash.hash_finish(hc)
+ return src.sourceid[source_set]
+end
+
+function svn.toresult(info, sourcename, sourceset, directory) --OK
+ -- <directory>/source/<sourcename>.tar.gz
+ -- <directory>/makefile
+ -- <directory>/licences
+ local rc, re
+ local e = new_error("converting result")
+ rc, re = check(info, sourcename, true)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local src = info.sources[sourcename]
+ -- write makefile
+ local makefile = "makefile"
+ local source = "source"
+ local sourcedir = string.format("%s/%s", directory, source)
+ local archive = string.format("%s.tar.gz", sourcename)
+ local fname = string.format("%s/%s", directory, makefile)
+ rc, re = e2lib.mkdir(sourcedir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ local f, msg = io.open(fname, "w")
+ if not f then
+ return false, e:cat(msg)
+ end
+ f:write(string.format(
+ ".PHONY:\tplace\n\n"..
+ "place:\n"..
+ "\ttar xzf \"%s/%s\" -C \"$(BUILD)\"\n",
+ source, archive))
+ f:close()
+ -- export the source tree to a temporary directory
+ local tmpdir = e2lib.mktempdir()
+ rc, re = svn.prepare_source(info, sourcename, sourceset, tmpdir)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- create a tarball in the final location
+ local archive = string.format("%s.tar.gz", src.name)
+ local tar_args = string.format("-C '%s' -czf '%s/%s' '%s'",
+ tmpdir, sourcedir, archive, sourcename)
+ rc, re = e2lib.tar(tar_args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ -- write licences
+ local destdir = string.format("%s/licences", directory)
+ local fname = string.format("%s/%s.licences", destdir, archive)
+ local licence_list = table.concat(src.licences, "\n") .. "\n"
+ rc, re = e2lib.mkdir(destdir, "-p")
+ if not rc then
+ return false, e:cat(re)
+ end
+ rc, re = e2lib.write_file(fname, licence_list)
+ if not rc then
+ return false, e:cat(re)
+ end
+ e2lib.rmtempdir(tmpdir)
+ return true, nil
+end
+
+function svn.update(info, sourcename)
+ local rc, re = svn.validate_source(info, sourcename)
+ if not rc then
+ e2lib.abort(re)
+ end
+ local e = new_error("updating svn source failed")
+ local src = info.sources[ sourcename ]
+ local working = string.format("%s/%s", info.root, src.working)
+ rc, re = e2lib.chdir(working)
+ if not rc then
+ return false, e:cat(re)
+ end
+ local args = "update"
+ rc, re = e2lib.svn(args)
+ if not rc then
+ return false, e:cat(re)
+ end
+ return true
+end
+
+--- validate source configuration, log errors to the debug log
+-- @param info the info table
+-- @param sourcename the source name
+-- @return bool
+function svn.validate_source(info, sourcename) --OK
+ local rc, re = generic_validate_source(info, sourcename)
+ if not rc then
+ -- error in generic configuration. Don't try to go on.
+ return false, re
+ end
+ local src = info.sources[ sourcename ]
+ if not src.sourceid then
+ src.sourceid = {}
+ end
+ local e = new_error("in source %s:", sourcename)
+ rc, re = source_apply_default_working(info, sourcename)
+ if not rc then
+ return false, e:cat(re)
+ end
+ e:setcount(0)
+ if not src.server then
+ e:append("source has no `server' attribute")
+ end
+ if not src.licences then
+ e:append("source has no `licences' attribute")
+ end
+ if not src.location then
+ e:append("source has no `location' attribute")
+ end
+ if src.remote then
+ e:append("source has `remote' attribute, not allowed for svn sources")
+ end
+ if not src.branch then
+ e:append("source has no `branch' attribute")
+ end
+ if not type(src.tag) == "string" then
+ e:append("source has no `tag' attribute or tag attribute has wrong type")
+ end
+ if not type(src.workingcopy_subdir) == "string" then
+ e2lib.warnf("WDEFAULT", "in source %s", sourcename)
+ e2lib.warnf("WDEFAULT",
+ " workingcopy_subdir defaults to the branch: %s", src.branch)
+ src.workingcopy_subdir = src.branch
+ end
+ if not src.working then
+ e:append("source has no `working' attribute")
+ end
+ local rc, re = transport.check_tool("svn")
+ if not rc then
+ e:cat(re)
+ end
+ if e:getcount() > 0 then
+ return false, e
+ end
+ return true, nil
+end
+
+e2scm.register("svn", svn)
--- /dev/null
+/*
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ testapi.c
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "e2api.h"
+
+
+int
+main(int argc, char *argv[])
+{
+ lua_State *lua;
+ char rpath[ PATH_MAX + 1 ];
+
+ if(argc > 1) strcpy(rpath, argv[ 1 ]);
+ else strcpy(rpath, ".");
+
+ lua = e2_init(rpath);
+
+ if(lua == NULL) {
+ fprintf(stderr, "[e2api] Error: %s\n", e2_error());
+ exit(EXIT_FAILURE);
+ }
+
+ if(e2_info(lua)) {
+ lua_pushnil(lua);
+
+ while (lua_next(lua, -2) != 0) {
+ printf("%s: %s\n", lua_tostring(lua, -2), lua_typename(lua, lua_type(lua, -1)));
+ lua_pop(lua, 1);
+ }
+
+ lua_pop(lua, 1);
+ }
+ else {
+ fprintf(stderr, "[e2api] Error: %s\n", e2_error());
+ exit(EXIT_FAILURE);
+ }
+
+ e2_exit(lua);
+ return 0;
+}
--- /dev/null
+--[[
+ e2factory, the emlix embedded build system
+
+ Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+ Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+
+ For more information have a look at http://www.e2factory.org
+
+ e2factory is a registered trademark by emlix GmbH.
+
+ This file is part of e2factory, the emlix embedded build system.
+
+ e2factory is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]
+
+-- testapi.lua
+--
+-- Test Lua e2 api
+
+
+dofile("e2api.lua")
+
+e2api.init(arg[ 1 ] or error("usage: lua e2api.lua PATH"))
+table.foreach(e2api.info(), print)
+
+rc = e2lib.callcmd_capture("exit 3")
+print(rc)
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+.PHONY: clean all install
+
+#
+# if they change the way Lua is built, we have a problem.
+#
+
+LUA_HEADER = lua.h lauxlib.h lualib.h luaconf.h
+
+lua-5.1.3:
+ tar -xzf $(LUA_SOURCE_PATH)/lua-5.1.3.tar.gz -C $(LUA_UNPACK_PATH); \
+ $(MAKE) -C $(LUA_UNPACK_PATH)/lua-5.1.3/src all \
+ MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E -ldl"; \
+
+all: lua-5.1.3
+
+install: all
+ mkdir -p $(DESTDIR)$(INCDIR)/lua-5.1.3; \
+ install -m755 $(LUA_UNPACK_PATH)/lua-5.1.3/src/lua $(DESTDIR)$(LIBEXECDIR)/e2-lua-5.1.3; \
+ install -m755 $(LUA_UNPACK_PATH)/lua-5.1.3/src/luac $(DESTDIR)$(LIBEXECDIR)/e2-luac-5.1.3; \
+ install -m644 $(LUA_UNPACK_PATH)/lua-5.1.3/src/lua.h $(DESTDIR)$(INCDIR)/lua-5.1.3; \
+ install -m644 $(LUA_UNPACK_PATH)/lua-5.1.3/src/lauxlib.h $(DESTDIR)$(INCDIR)/lua-5.1.3; \
+ install -m644 $(LUA_UNPACK_PATH)/lua-5.1.3/src/lualib.h $(DESTDIR)$(INCDIR)/lua-5.1.3; \
+ install -m644 $(LUA_UNPACK_PATH)/lua-5.1.3/src/luaconf.h $(DESTDIR)$(INCDIR)/lua-5.1.3; \
+
+uninstall:
+ rm -f $(DESTDIR)$(LIBEXECDIR)/e2-lua-5.1.3; \
+ rm -f $(DESTDIR)$(LIBEXECDIR)/e2-luac-5.1.3; \
+ rm -fr $(DESTDIR)$(LIBEXECDIR)/lua-5.1.3; \
+ for x in $(LUA_HEADER) ; do \
+ rm -f $(DESTDIR)$(INCDIR)/lua-5.1.3/$$x ; \
+ done
+
+doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+install-doc:
+ for s in $(SUBDIRS) ; do \
+ $(MAKE) -C $$s $@ ;\
+ done
+
+clean:
+ rm -fr $(LUA_ALL_VERSIONS:%=lua-%)
--- /dev/null
+# make.vars -*- makefile -*-
+DETECT_TOOL = $(TOPLEVEL)/scripts/detect_tool
+
+E2_VERSION = $(shell cat $(TOPLEVEL)/version)
+E2_COMMIT ?= $(shell cd $(TOPLEVEL);scripts/buildversion.sh)
+PROJECTDIR ?= $(shell cd $(TOPLEVEL);scripts/e2-locate-project-root 2>/dev/null)
+E2_SYNTAX = $(shell cat $(TOPLEVEL)/syntax)
+E2DATA = /mnt/e2data
+export E2_VERSION E2_COMMIT E2DATA PROJECTDIR E2_SYNTAX
+
+DESTDIR =
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib/e2
+LIBEXECDIR = $(PREFIX)/libexec/e2
+INCDIR = $(PREFIX)/include/e2
+SHAREDIR = $(PREFIX)/share
+LOCALSTATEDIR = /var/lib/e2factory
+SYSCONFDIR = /etc/e2
+MANDIR = $(SHAREDIR)/man
+DOCDIR = $(SHAREDIR)/doc/e2
+TOOLDIR = $(LIBEXECDIR)/tools
+export PREFIX BINDIR LIBDIR INCDIR MANDIR TOOLDIR LIBEXECDIR LOCALSTATEDIR
+export SYSCONFDIR
+
+LOCALPREFIX = $(PROJECTDIR)/.e2
+LOCALBINDIR = $(LOCALPREFIX)/bin
+LOCALLIBDIR = $(LOCALPREFIX)/lib/e2
+LOCALMAKDIR = $(LOCALPREFIX)/lib/make
+export LOCALPREFIX LOCALBINDIR LOCALLIBDIR LOCALMAKDIR
+
+ARCH = $(shell uname -m)
+LUA_VERSION = 5.1.3
+LUA_ALL_VERSIONS = 5.1.2 5.1.3
+LUAC = $(LIBEXECDIR)/e2-luac-$(LUA_VERSION)
+ifdef LUA_UNPACK_PATH
+BUILD_LUAC = $(LUA_UNPACK_PATH)/lua-$(LUA_VERSION)/src/luac
+BUILD_LUA_CPPFLAGS = -I $(LUA_UNPACK_PATH)/lua-$(LUA_VERSION)/src
+else
+BUILD_LUAC = $(TOPLEVEL)/lua/lua-$(LUA_VERSION)/src/luac
+BUILD_LUA_CPPFLAGS = -I $(TOPLEVEL)/lua/lua-$(LUA_VERSION)/src
+endif
+LUA_CPPFLAGS = -I $(INCDIR)/lua-$(LUA_VERSION)
+UPSTREAM = $(E2DATA)/upstream
+BASE_PROJECT_PATH = $(E2DATA)/projects/emlix/emlix-base-project/proj/emlix-base-project.git
+BINARY_STORE = $(E2DATA)/projects/emlix/e2/files
+LUA_SOURCE_PATH ?= $(TOPLEVEL)
+LUA_UNPACK_PATH ?= .
+
+export LUA_VERSION UPSTREAM BASE_PROJECT_PATH LUAC BUILD_LUAC ARCH BINARY_STORE
+
+CC = gcc
+CFLAGS = -g -Wall
+CPPFLAGS = -D E2_COMMIT="\"$(E2_COMMIT)\"" \
+ -D E2_VERSION="\"$(E2_VERSION)\"" \
+ -DBINDIR="\"$(BINDIR)\"" \
+ -DLIBDIR="\"$(LIBDIR)\"" \
+ -DLIBEXECDIR="\"$(LIBEXECDIR)\"" \
+ -DINCDIR="\"$(INCDIR)\"" \
+ -DMANDIR="\"$(MANDIR)\"" \
+ -DTOOLDIR="\"$(TOOLDIR)\""
+
+LDFLAGS =
+
+export CC CFLAGS CPPFLAGS LDFLAGS
+
+# build-time detection for tools used by the setuid root code
+ENV_TOOL = $(shell $(DETECT_TOOL) env)
+CHROOT_TOOL = $(shell $(DETECT_TOOL) chroot)
+TAR_TOOL = $(shell $(DETECT_TOOL) tar)
+CHOWN_TOOL = $(shell $(DETECT_TOOL) chown)
+RM_TOOL = $(shell $(DETECT_TOOL) rm)
+export ENV_TOOL TAR_TOOL CHROOT_TOOL TAR_TOOL CHOWN_TOOL RM_TOOL
+
+E2_SU_CFLAGS = -D CHROOT_TOOL="\"$(CHROOT_TOOL)\"" \
+ -D TAR_TOOL="\"$(TAR_TOOL)\"" \
+ -D CHOWN_TOOL="\"$(CHOWN_TOOL)\"" \
+ -D RM_TOOL="\"$(RM_TOOL)\""
+
+E2_GROUP = ebs
+export E2_GROUP
+
+# vim: ft=make
--- /dev/null
+#E=echo
+E=""
+cat <<EOF
+Release Checklist:
+
+ * is the configuration syntax list up-to-date? (see syntax, local/e2tool.lua)
+
+EOF
+read -p "type yes to proceed> " OK
+if [ "$OK" != "yes" ] ; then
+ exit 1
+fi
+echo "Example: 2.2pre7"
+read -p "VERSION:" VERSION
+RELEASE_NAME="e2factory-$VERSION"
+echo $VERSION >./version
+vi Changelog # edit the release string
+echo ==================
+echo Changelog:
+head Changelog
+echo Version: $VERSION
+echo Release Name: $RELEASE_NAME
+echo ==================
+read -p "commit, tag, push?"
+$E git commit -m "release $RELEASE_NAME" version Changelog
+$E git tag "$RELEASE_NAME"
+$E git push origin "$RELEASE_NAME"
+
+VERSION=${VERSION}-wip
+RELEASE_NAME="e2factory-$VERSION"
+echo $VERSION >./version
+mv Changelog Changelog.tmp
+cat - Changelog.tmp > Changelog <<EOF
+$RELEASE_NAME
+
+EOF
+vi Changelog # edit the release string: wip again
+echo ==================
+echo Changelog:
+head Changelog
+echo Version: $VERSION
+echo Release Name: $RELEASE_NAME
+echo ==================
+read -p "commit, push?"
+$E git commit -m "work in progress $RELEASE_NAME" version Changelog
+$E git push
+
--- /dev/null
+#!/bin/sh -e
+#
+# buildversion.sh (C) 2007 by emlix GmbH
+#
+# syntax:
+# buildversion.sh
+#
+# if .git is available use git describe to get a version identifier.
+# otherwise use the first line of ./VERSION as the version identifier.
+#
+# print the version identifier to stdout
+
+if [ -d ./.git ] ; then
+ VERSION=$(git describe --tags)
+elif [ -f ./version ] ; then
+ VERSION=$(head -n1 version)
+else
+ echo >&2 "can't find version identifier"
+ exit 1
+fi
+echo "$VERSION"
+exit 0
--- /dev/null
+#!/bin/bash
+# Help finding tools used by the root code by enhancing the standard search
+# path. This will do in almost all cases.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin:$PATH" which "$1" && exit 0
+echo >&2 "missing Tool: $1"
--- /dev/null
+#!/bin/sh
+export LC_ALL=C
+while [ '!' -f .e2/e2version ] ; do
+ if [ "$PWD" = "/" ] ; then
+ echo >&2 \
+ "e2-locate-project-root: Not in a project environment."
+ exit 1
+ fi
+ cd ..
+done
+echo $PWD
--- /dev/null
+#!/bin/bash
+
+function die() {
+ echo "(genscript.sh) Error: $1"
+ exit 1
+}
+
+set -e
+test -n "$E2_COMMIT" || die "E2_COMMIT not set"
+test -n "$E2_VERSION" || die "E2_VERSION not set"
+test -n "$E2_SYNTAX" || die "E2_SYNTAX not set"
+test -n "$PREFIX" || die "PREFIX not set"
+test -n "$BINDIR" || die "BINDIR not set"
+test -n "$LIBDIR" || die "LIBDIR not set"
+test -n "$LIBEXECDIR" || die "LIBEXECDIR not set"
+test -n "$LOCALSTATEDIR" || die "LOCALSTATEDIR not set"
+test -n "$SYSCONFDIR" || die "SYSCONFDIR not set"
+test -n "$E2DATA" || die "E2DATA not set"
+test -n "$TOOLDIR" || die "TOOLDIR not set"
+test -n "$LUA_VERSION" || die "LUA_VERSION not set"
+test -n "$ARCH" || die "ARCH not set"
+test -n "$BINARY_STORE" || die "BINARY_STORE not set"
+test -n "$E2_GROUP" || die "E2_GROUP not set"
+test -n "$ENV_TOOL" || die "ENV_TOOL not set"
+test -n "$CHROOT_TOOL" || die "CHROOT_TOOL not set"
+test -n "$TAR_TOOL" || die "TAR_TOOL not set"
+test -n "$CHOWN_TOOL" || die "CHOWN_TOOL not set"
+test -n "$RM_TOOL" || die "RM_TOOL not set"
+sed -e s/"@E2_COMMIT@"/"$E2_COMMIT"/g \
+ -e s/"@E2_VERSION@"/"$E2_VERSION"/g \
+ -e s/"@E2_SYNTAX@"/"$E2_SYNTAX"/g \
+ -e s,"@E2_E2DATA@","$E2DATA",g \
+ -e s,"@LIBDIR@","$LIBDIR",g \
+ -e s,"@LIBEXECDIR@","$LIBEXECDIR",g \
+ -e s,"@BINDIR@","$BINDIR",g \
+ -e s,"@LOCALSTATEDIR@","$LOCALSTATEDIR",g \
+ -e s,"@SYSCONFDIR@","$SYSCONFDIR",g \
+ -e s,"@TOOLDIR@","$TOOLDIR",g \
+ -e s,"@LUA_VERSION@","$LUA_VERSION",g \
+ -e s,"@ARCH@","$ARCH",g \
+ -e s,"@BINARY_STORE@","$BINARY_STORE",g \
+ -e s,"@E2_GROUP@","$E2_GROUP",g \
+ -e s,"@SERVER_NAME@","$SERVER_NAME",g \
+ -e s,"@SERVER_PORT@","$SERVER_PORT",g \
+ -e s,"@ENV_TOOL@","$ENV_TOOL",g \
+ -e s,"@CHROOT_TOOL@","$CHROOT_TOOL",g \
+ -e s,"@TAR_TOOL@","$TAR_TOOL",g \
+ -e s,"@CHOWN_TOOL@","$CHOWN_TOOL",g \
+ -e s,"@RM_TOOL@","$RM_TOOL",g \
+ -e s,"@E2_PREFIX@","$PREFIX",g $1 >$2 \
--- /dev/null
+#!/bin/sh -e
+#
+# release.sh (C) 2007 by emlix GmbH
+#
+# syntax:
+# release.sh <release name>
+#
+# this script creates a release as follows:
+#
+# 1. write the release name to ./VERSION
+# 2. create a commit tracking the change to the VERSION file
+# 3. create a tag named VERSION
+#
+# The VERSION file is used by buildversion.sh to create a name
+# describing the state of the working copy as detailed as possible.
+#
+
+VERSION="$1"
+
+if [ -z "$VERSION" ] ; then
+ echo "Error: empty version"
+ exit 1
+fi
+
+TAG="$VERSION"
+
+if git tag -l "^$TAG\$" ; then
+ echo "Error: tag exists: $TAG"
+ exit 1
+fi
+
+cat >VERSION <<EOF
+$VERSION
+EOF
+
+git add VERSION
+git commit -s -m "version $TAG" VERSION
+git tag "$TAG"
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+BINDIR = /root/bin
+
+install:
+ @echo "Please use the 'server-install' target."
+
+server-install:
+ install -m 755 upstream $(BINDIR)/e2fix-upstream
+ install -m 755 results $(BINDIR)/e2fix-results
+ install -m 755 e2nightly $(BINDIR)/e2nightly
+ install -m 755 e2data_backup $(BINDIR)/e2data_backup
+
--- /dev/null
+#!/bin/bash
+rsync --archive --update --delete /data/e2data/ /data/tmp/e2data_backup/
--- /dev/null
+#!/bin/bash
+UPSTREAM=/data/e2data/upstream /root/bin/e2fix-upstream | bash
+RESULTS=/data/e2data/results /root/bin/e2fix-results | bash
--- /dev/null
+#!/bin/sh
+# print a list of commands that fix permissions on the result server.
+set -e
+if [ -z "$RESULTS" ] ; then
+ RESULTS=/mnt/e2data/results
+fi
+if [ ! -f $RESULTS/.e2-results ] ; then
+ echo >&2 "Error: $RESULTS does not point to the upstream server"
+ exit 1
+fi
+find $RESULTS -type d -not -group 203 -exec echo chgrp 203 {} \;
+find $RESULTS -type d -not -perm 2775 -exec echo chmod 2775 {} \;
+find $RESULTS -type f -not -group 203 -exec echo chgrp 203 {} \;
+find $RESULTS -type f -not -perm 0664 -exec echo chmod 0664 {} \;
+exit 0
--- /dev/null
+#!/bin/sh
+# print a list of commands that fix permissions on the upstream server.
+set -e
+if [ -z "$UPSTREAM" ] ; then
+ UPSTREAM=/mnt/e2data/upstream
+fi
+if [ ! -f $UPSTREAM/.e2-upstream ] ; then
+ echo >&2 "Error: $UPSTREAM does not point to the upstream server"
+ exit 1
+fi
+find $UPSTREAM -type d -not -group 203 -exec echo chgrp 203 {} \;
+find $UPSTREAM -type d -not -perm 2775 -exec echo chmod 2775 {} \;
+find $UPSTREAM -type f -not -group 203 -exec echo chgrp 203 {} \;
+find $UPSTREAM -type f -not -perm 0444 -exec echo chmod 0444 {} \;
+exit 0
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+#### Makefile for build-number and log server
+
+
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+SERVER_VERSION = 0.1
+CFLAGS = -g -DVERSION=\"$(SERVER_VERSION)\" -DSHAREDIR=\"$(SHAREDIR)\" \
+ -DLIBEXECDIR=\"$(LIBEXECDIR)\" -DLUA_VERSION=\"$(LUA_VERSION)\"
+
+.PHONY: all clean install
+
+all: web/store-run-log.lua web/retrieve-build-numbers.lua
+
+clean:
+ rm -f *~ *.o web/*.lua
+
+web/%.lua: web/%.lua.in
+ sed -e 's,@LOGDIR@,$(SERVER_LOGDIR),g' \
+ <$< >$@
+
+install: all
+ mkdir -p $(DESTDIR)$(SHAREDIR)/e2-server/web
+ install -m644 web/*.lua $(DESTDIR)$(SHAREDIR)/e2-server/web
--- /dev/null
+-- retrieve-build-numbers.lua - fetch current or new build-numbers -*- Lua -*-
+--
+-- method: POST
+--
+-- body: result-lines of the form "<resultname> <buildid*> <buildnumber>"
+--
+-- query string:
+--
+-- project=<projectname>
+--
+-- response: same format as request body, with updated or same contents
+
+
+local cl = os.getenv("CONTENT_LENGTH")
+
+local function fail(msg)
+ print("Status: 500 Internal Server Error\n\n" .. msg)
+ os.exit(1)
+end
+
+if not cl then fail("no content-length") end
+
+local function readbn(str)
+ local t = {}
+ for ln in string.gmatch(str, "[^\n]*\n") do
+ if not string.match(ln, "^%s*$") then
+ local res, bid, status, num =
+ string.match(ln, "([-%w_]+)%s+(%x+)%s+(%S+)%s+(%d+)")
+ if res then
+ t[ res ] = { id = bid, num = num, status = status }
+ else
+ fail("invalid request line: " .. ln)
+ end
+ end
+ end
+ return t
+end
+
+local function writebn(f, t)
+ for k, v in pairs(t) do
+ f:write(k, " ", v.id, " ok ", v.num, "\n")
+ end
+end
+
+local bytes = tonumber(cl)
+
+if bytes == 0 then
+ fail("zero content-length")
+end
+
+local req = io.read(bytes) or fail("empty request")
+local usernums = readbn(req)
+local query = {}
+
+for k, v in string.gmatch(os.getenv("QUERY_STRING"), "(%w+)=([-%w_]+)") do
+ query[ k ] = v
+end
+
+if not query.project then fail("no project given") end
+
+local logdir = "@LOGDIR@/" .. query.project
+local numfile = logdir .. "/build-numbers"
+
+os.execute("mkdir -p '" .. logdir .. "'")
+
+local currentnums
+local f, msg = io.open(numfile, "r")
+local changed = not f
+
+if not f then
+ currentnums = usernums
+else
+ local i = f:read("*a")
+ if not i then
+ fail("empty request")
+ end
+ f:close()
+ currentnums = readbn(i)
+end
+
+for k, v in pairs(usernums) do
+ local r = currentnums[ k ]
+ if r then
+ if r.id == v.id and v.status ~= "force" then
+ v.num = r.num
+ else
+ r.num = r.num + 1
+ r.id = v.id
+ changed = true
+ v.num = r.num
+ end
+ else
+ currentnums[ k ] = v
+ end
+end
+
+if changed then
+ f, msg = io.open(numfile, "w")
+ if not f then
+ fail("can not write `" .. numfile .. "': " .. msg)
+ end
+ writebn(f, currentnums)
+ f:close()
+end
+
+print("Content-type: text/plain\n")
+
+writebn(io.stdout, usernums)
--- /dev/null
+-- store-run-log.lua - store run.log from client -*- Lua -*-
+--
+-- method: POST
+--
+-- body: log-file section
+--
+-- query string:
+--
+-- host=<hostname>
+-- user=<username>
+-- project=<projectname>
+
+
+print("Content-type: text/plain\n")
+
+local cl = os.getenv("CONTENT_LENGTH")
+
+assert(cl)
+
+local log = io.read(tonumber(cl))
+local query = {}
+
+for k, v in string.gmatch(os.getenv("QUERY_STRING"), "(%w+)=([-%w_]+)") do
+ query[ k ] = v
+end
+
+assert(query.host)
+assert(query.user)
+assert(query.project)
+
+local logdir = "@LOGDIR@/" .. query.project .. "/" .. query.host
+local logfile = logdir .. "/" .. query.user .. ".log"
+
+os.execute("mkdir -p '" .. logdir .. "'")
+
+local function writelog(logfile)
+ local f, msg = io.open(logfile, "a")
+ if not f then
+ error("can not open log file `" .. logfile .. "': " .. msg)
+ end
+ f:write(log or "")
+ f:close()
+end
+
+writelog(logfile)
+writelog("@LOGDIR@/all.log")
+
+print("ok")
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# makefile for emlix-specific global installations
+
+TOPLEVEL = ..
+
+include $(TOPLEVEL)/make.vars
+
+.PHONY: all clean install uninstall
+
+all:
+
+clean:
+
+install: all
+ install -m 755 -d /etc/e2/templates/proj
+ install -m 644 proj/config /etc/e2/templates/proj/config
+ install -m 644 proj/chroot /etc/e2/templates/proj/chroot
+ install -m 644 proj/licences /etc/e2/templates/proj/licences
+ install -m 644 proj/env /etc/e2/templates/proj/env
+ install -m 644 gitignore /etc/e2/templates/gitignore
+
+install-local: all
+ install -m 755 -d $(LOCALLIBDIR)/templates
+ install -m 644 local/build-script $(LOCALLIBDIR)/templates/
+ install -m 644 local/result $(LOCALLIBDIR)/templates/
+ install -m 644 local/source.files $(LOCALLIBDIR)/templates/
+ install -m 644 local/source.git $(LOCALLIBDIR)/templates/
+ install -m 644 local/source.cvs $(LOCALLIBDIR)/templates/
+ install -m 644 local/source.svn $(LOCALLIBDIR)/templates/
+
+%.sh: %.sh.in
+ $(TOPLEVEL)/scripts/genscript.sh $< $@
+
+uninstall:
+ # do not remove possibly valuable templates
+
+doc:
+
+install-doc:
+
--- /dev/null
+in/*
+out/*
+log/*
+.e2/bin
+.e2/e2
+.e2/lib
+.e2/project-location
--- /dev/null
+# e2factory build script template
+#
+# The build system provides the following variables for use in build scripts:
+# - E2_TMPDIR pointing to a temporary directory with a directory
+# structure
+# - E2_RESULT the name of the result currently building
+# - E2_BUILD_NUMBER the build number, 0 by default
+#
+# Additional variables can be provided in init files.
+# Variables are not exported unless the init files do.
+#
+# The following directory structure is available, relative to TMPDIR
+#
+# ./build/<sourcename> sources are located here
+# ./root shall be used as the destination directory when
+# installing software for packaging
+# ./env files containing environment variables are located here
+# ./init init scripts are located here
+# ./script this directory holds the build driver and the build
+# script
+# ./in This directory is available but unused
+# ./dep/<dependency> These directories hold result files from dependency
+# results
+# ./out Files that are left here are packaged into the result
+# finally.
+#
--- /dev/null
+-- e2factory result configuration template --
+
+e2result {
+ -- a list of sources
+ sources = { },
+ -- a list of dependencies
+ depends = { },
+ -- a list of chroot groups
+ chroot = { },
+ -- use the collect_project feature for this result?
+ collect_project = false,
+ -- collect_project_default_result = "<result>",
+}
--- /dev/null
+-- e2factory cvs source configuration template --
+
+e2source {
+ -- the source name
+ -- (is derived from the config file path if only one source is
+ -- specified per file)
+ name = "name",
+ -- the source type
+ type = "cvs",
+ -- a list of licences from proj/licences
+ licences={ },
+ -- cvs specific attributes
+ -- server name
+ server = "proj-storage",
+ -- location of cvsroot relative to the server
+ cvsroot = "cvs",
+ -- cvs module
+ module = "hellocvs",
+ -- cvs branch (may be HEAD)
+ branch = "HEAD",
+ -- cvs tag
+ tag = "zwei",
+ -- working directory (is set to in/<sourcename> by default)
+ --working = "in/hellocvs",
+}
--- /dev/null
+-- e2factory files source configuration template --
+
+e2source {
+ -- the source name
+ -- (is derived from the config file path if only one source is specified
+ -- per file)
+ name = "name",
+ -- the source type
+ type = "files",
+ -- a list of licences from proj/licences
+ licences = { },
+ -- the server. Files can specify their own one or inherit this one.
+ server = "upstream",
+ -- the file list
+ file = {
+ -- each file must be configured in a table like this
+ {
+ -- server name (or inherit from top-level)
+ -- server = "upstream",
+ -- file location relative to the server
+ location = "h/hello/1.0/hello-1.0.tar.gz",
+ -- select one of unpack, patch and copy
+ -- unpacking creates this directory
+ unpack = "hello-1.0",
+ -- file is a patch. Apply with patch -p1
+ --patch = "1",
+ -- copy to a directory relative to the source tree
+ --copy = ".",
+ },
+ -- insert more files here
+ },
+}
--- /dev/null
+-- -*- Lua -*-
+
+e2source {
+ -- the source name
+ -- (is derived from the config file path if only one source is
+ -- specified per file)
+ name = "name",
+ -- the source type
+ type = "git",
+ -- a list of licences from proj/licences
+ licences = { },
+ -- git specific attributes
+ -- server name
+ server = "proj-storage",
+ -- the repository location relative to the server
+ location = "git/<template>.git",
+ -- git branch name
+ branch = "master",
+ -- git tag names, the first one is used for building
+ tag = "<template>",
+}
--- /dev/null
+-- e2factory subversion source configuration template --
+
+e2source {
+ -- the source name
+ -- (is derived from the config file path if only one source is
+ -- specified per file)
+ name = "name",
+ -- the source type
+ type = "svn",
+ -- a list of licences from proj/licences
+ licences={ },
+ -- svn specific attributes
+ -- server name
+ server = "proj-storage",
+ -- location of the svn tree relative to the server
+ location = "svn/hellosvn",
+ -- svn branch location relative to the location
+ branch = "branches/hello-1.0",
+ -- svn tag location relative to the location
+ tag = "tags/hello-1.0",
+ -- take this subdirectory from the working copy when the working-copy
+ -- is used. (Defaults to the branch setting.)
+ --workingcopy_subdir = "branches/hello-1.0",
+}
--- /dev/null
+-- e2factory chroot configuration file --
+local cp = "emlix/chroot-i386/tags/20080916-stable-2/"
+e2chroot {
+ default_groups = {
+ "base",
+ },
+ groups = {
+ {
+ name = "base",
+ server = "chroot",
+ files = {
+ cp.."base.tar.gz",
+ },
+ },
+ },
+}
--- /dev/null
+e2project {
+ -- these must be configured
+ name = "project",
+ release_id = "release-id",
+ default_results = { },
+
+ -- these may be configured optionally
+ --chroot_arch = "x86_32", -- or: x86_64
+}
--- /dev/null
+-- e2factory environment configuration file --
+env {
+ -- global variables
+ -- key = "val",
+
+ -- result specific variables (only available in <result>)
+ -- <result> = {
+ -- key = "val",
+ -- },
+}
--- /dev/null
+-- e2factory licence configuration file --
+e2licence {
+}
--- /dev/null
+#
+# e2factory, the emlix embedded build system
+#
+# Copyright (C) 2007-2009 Gordon Hecker <gh@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2009 Oskar Schirmer <os@emlix.com>, emlix GmbH
+# Copyright (C) 2007-2008 Felix Winkelmann, emlix GmbH
+#
+# For more information have a look at http://www.e2factory.org
+#
+# e2factory is a registered trademark by emlix GmbH.
+#
+# This file is part of e2factory, the emlix embedded build system.
+#
+# e2factory is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+default: help
+
+help:
+ @echo Targets: check
+
+e2.conf: e2.conf.in
+ sed s,TESTDIR,$(shell pwd)/tmp/work, e2.conf.in > e2.conf
+
+check: e2.conf
+ @echo running tests...
+ @env -i HOME=$(HOME) USER=$(USER) TESTS=$(TESTS) bash runtests.sh
+
+clean:
+ sudo rm -fr e2
+ rm -fr tmp
+ rm -fr log
+ rm -f e2.conf
--- /dev/null
+#!/bin/bash
+export PATH=$PWD/e2/bin:/bin:/usr/bin:/sbin:/usr/sbin
+declare -i pass
+declare -i fail
+pass=0
+fail=0
+if [ -z "$TESTS" ] ; then
+ TESTS=$(cd tests; ls *.test)
+fi
+rm -fr log
+mkdir log
+LOG=$PWD/log/test.log
+export E2_CONFIG=$PWD/e2.conf
+export E2_LOCAL_BRANCH="$(git branch | grep '^*' | sed s/'^* '/''/)"
+echo "Testing: $(date)" >$LOG
+for i in $TESTS ; do
+ rm -fr ./tmp
+ mkdir ./tmp
+ cd ./tmp
+ bash -e -x ../tests/$i >../log/$i.log 2>&1
+ r=$?
+ if [ "$r" == "0" ] ; then
+ pass=$pass+1
+ printf "%-40s %s\n" "$i" "OK" | tee -a $LOG
+ else
+ fail=$fail+1
+ printf "%-40s %s\n" "$i" "FAIL" | tee -a $LOG
+ fi
+ cd ..
+done
+printf "Pass: %d\n" $pass | tee -a $LOG
+printf "Fail: %d\n" $fail | tee -a $LOG
--- /dev/null
+source ../tests/init.lib
+test -d work/projects/e2testsuite/tp
--- /dev/null
+e2-fetch-project --tooldebug projects:e2testsuite/tp work/tp
--- /dev/null
+source ../tests/init.lib
+source ../tests/fetch.lib
+test -d work/tp
--- /dev/null
+mkdir work
+cd work
+mkdir projects
+mkdir upstream
+mkdir results
+cd ..
+mkdir -p work/projects/emlix/null/git
+git clone --bare ../../ work/projects/emlix/e2/git/e2.git
+e2-create-project --tooldebug projects:e2testsuite/tp
--- /dev/null
+source ../tests/init.lib
+source ../tests/fetch.lib
+source ../tests/configure.lib
+source ../tests/new_files_source.lib
+source ../tests/use_files_source.lib
+cd work/tp
+e2-ls-project
--- /dev/null
+source ../tests/init.lib
+source ../tests/fetch.lib
+source ../tests/configure.lib
+source ../tests/new_files_source.lib
--- /dev/null
+pushd work/tp
+mkdir foo
+echo foo > foo/foo.txt
+tar -czf foo.tar.gz foo
+sha1sum foo.tar.gz > CHECKSUM
+e2-new-source --files --vall \
+ upstream:f/foo/1.0 file://$PWD/foo.tar.gz file://$PWD/CHECKSUM
+popd
--- /dev/null
+source ../tests/init.lib
+source ../tests/fetch.lib
+source ../tests/configure.lib
+source ../tests/new_files_source.lib
+source ../tests/use_files_source.lib
--- /dev/null
+pushd work/tp
+e2-use-source --files --vall upstream:f/foo/1.0/foo.tar.gz foo
+test -f src/foo/config
+popd