diff options
143 files changed, 11208 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49ed84d --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +*~ +#* + +/.var.* +.tmp* + +# Files generated for dist from git +.srcfiles.mk +/.srcversion-libretools.mk +/.srcversion-devtools.mk + +# The top-level distdir +/*-*/ + +# File extensions +*.ugly +*.tar.gz +*.pot +*.1 +*.2 +*.3 +*.4 +*.5 +*.6 +*.7 +*.8 +*.1.html +*.2.html +*.3.html +*.4.html +*.5.html +*.6.html +*.7.html +*.8.html @@ -0,0 +1,10 @@ +Daniel Molina (lluvia) +Esteban Carnevale <alfplayer@mailoo.org> +Joseph Graham (Xylon) <joe@t67.eu> +Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +Luke Shumaker <lukeshu@sbcglobal.net> +Michał Masłowski <mtjm@mtjm.eu> +Nicolás Reynolds <fauno@parabola.nu> + +As well as authors of code taken from Arch Linux's "devtools" and +"dbscripts", and Pacman's "makepkg". @@ -0,0 +1,9 @@ +Different parts of libretools are either the GNU General Public +License version 2 or 3; sometimes with, sometimes without the option +to use a later version of the license. Which one is specified on a +per-file basis; look at the source. In addition to a full copyright +and license statement, each file contains a "License:" line to make +searching easy. + +Full copies of the GNU General Public License versions 2 and 3 are +included as `COPYING-GPLv2` and `COPYING-GPLv3`, respectively. diff --git a/COPYING-GPLv2 b/COPYING-GPLv2 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING-GPLv2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/COPYING-GPLv3 b/COPYING-GPLv3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING-GPLv3 @@ -0,0 +1,674 @@ + 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 + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program 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/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/HACKING/build-system.md b/HACKING/build-system.md new file mode 100644 index 0000000..93770cc --- /dev/null +++ b/HACKING/build-system.md @@ -0,0 +1,24 @@ +The build system is built around an automake-like system for GNU Make +that I wrote. It is documented in `automake.txt`. It provides all of +the standard targets and such; you tell it what to do by setting a +series of `am_whatever` variables. I'm just going to call it +"automake" here. + +I also hook into non-exposed parts of automake with a couple of +`_am_whatever` variables. Hopefully I will come up with good ways to +expose the needed functionality in the future. + +There are a couple of variables that get automatically set from git. +This happens by `include`ing a hidden makefile that sets them; if +`$(topsrcdir)/.git` exists, it knows to use git to generate it; +otherwise it expects the file to exist: + +| variable | file | +|--------------------+----------------------------------------| +| srcfiles | $(srcdir)/.srcfiles.mk | +| LIBRETOOLS_VERSION | $(topsrcdir)/.srcversion-libretools.mk | +| LIBRETOOLS_COMMIT | $(topsrcdir)/.srcversion-libretools.mk | +| DEVTOOLS_VERSION | $(topsrcdir)/.srcversion-devtools.mk | +| DEVTOOLS_COMMIT | $(topsrcdir)/.srcversion-devtools.mk | + +`srcfiles` basically becomes `am_src_files`. diff --git a/HACKING/code-quality.md b/HACKING/code-quality.md new file mode 100644 index 0000000..8efd561 --- /dev/null +++ b/HACKING/code-quality.md @@ -0,0 +1,135 @@ +Code content +============ + +Be aware of the `librelib(7)` library suite, which lives `src/lib`. +It is a suite of Bash libraries that will help you out. Most of the +people looking at the libretools code are familiar with the `messages` +part of it, which actually contains a bunch of utility routines, not +just message printing. There is also a library for dealing with +`blacklist.txt`, and one for loading configuration files and +PKGBUILDs. These are common tasks, but are tricky to handle +consistently--the libraries are there to make things easier. Take a +look at the man pages. + +Message printing: All of the message printing routines, except for +`term_title` and `flag`, take printf-type arguments. Take advantage +of that; don't use string interpolation (don't do `"foo ${var} +bar"`). The reason for this is that if you don't do string +interpolation, messages can be automatically internationalized. +(Internationalization is incomplete at the momement) + +Message printing: The in `--help`/`-h` text, use `print` to print +lines that should not wrap, `echo` to print blank lines, `prose` to +print paragraphs, `bullet` to print bullet points, and `flag` to print +option flags. The text should follow this general format: + + print "Usage: %s [OPTIONS] VARS_ARE_UNDERSCORE_AND_CAPITAL" "${program_name}" + print "One line description of program, no period" + echo + prose "More details. This is a paragraph." + echo + print "Options:" + flag "-h" "Show this message" + +In the "Usage:" line, use printf `%s` and the value `"${0##*/}"` to +determine the program name at runtime. + +There used to be guidelines for how to align the option flags and +descriptions, but now the `flag` command exists takes care of it for +you. Yay for things being easier! + +When using `set -u`, `set -e`, or `trap`, you should also use `set -E` +to have the error handling be passed down to subshells. + +Feel free to use `set -e` (fail on error), but be careful of the +caveats (there are a bunch of them); don't assume all errors are +checked because of it. + +Use `set -u` if you can; it makes using an unset variable an error. + - If a variable not being set is valid (perhaps a configuration + option), use `${var:-}` when accessing it to suppress the error. + - An empty array counts as unset, so if you have an array that may be + empty, use `${var+"${var[@]}"}` (don't put quotes around the outer + pair of braces) to only access it if it's non-empty. + - The reason for this is that a normal string variable is basically + an array with length=1; an unset variable looks like an array + with length=0. Weird stuff. + +In the shebang, use `#!/usr/bin/env bash`. This allows us to not +hardcode the location of bash (I'm not sure why this is useful for +something distro-dependent like libretools, but fauno seems to have a +use-case for it). + +In the shebang, don't pass flags to bash, besides breaking `env` +(above), it means people will make mistakes when debugging, and +running things with `bash FILENAME`. Instead, use `set` to adjust the +flags inside of the program. + +Obey `$TMPDIR`. It's usually as easy as passing `--tmpdir` to +`mktemp`. + +Use `trap` to clean up your temporary files. This way, even if your +program terminates early, things will be cleaned up. + +Bash best practices +=================== + +Basically, know what you are doing, and be safe with it. The problem +is that most people don't know about safe bash scripting. + +A lot of people look at the "Advanced Bash Scripting" ebook--DO NOT do +that, it is trash... though it contains a "reference card" page that +may be useful and isn't trash. + +Take a look at Gentoo's Bash guidelines +<http://devmanual.gentoo.org/tools-reference/bash/index.html>. +They're pretty good, and cover most of the "gotcha's" about Bash +syntax. It mentions but discourages the use of Bash 3 +features... why? Who still uses Bash 2? Feel free to use Bash 4 +features! + +I wrote an article on Bash arrays +<https://lukeshu.com/blog/bash-arrays.html>. A lot of people think +they're tricky, but they're simple once you know how they work. It's +short enough that you should read the whole thing. Know the +difference between `"${array[@]}"` and `"${array[*]}"`. And I'll say +it again here, ALWAYS wrap those in double quotes; there is no reason +I can think of that the unquoted behavior would ever be the correct +thing. + +My brief rules of thumb: + + - Quote every variable. + - That includes arrays: `"${array[@]}"` and `"${array[*]}"`. + - In most (but not all!) cases inside of `[[ ... ]]` conditions, + variables don't need to be quoted. When in doubt, quote them. + - When assigning one variable to another, you don't need quotes; + you don't need quotes for `foo=$bar` + - Try to avoid global variables; declare all variables in functions + with `local`. + - Or `declare`; inside of a function, unless you pass the `-g` + flag, `declare` makes the variable local. + - Use `local VAR` before a `for VAR in LIST` loop--the variable is created in the + current scope, not the scope of the loop. + - Feeding input to `while` loops is weird because of how subshells + work: + + # Input from a file + # BAD + cat file | while read line; do + ... + done + # GOOD + while read line; do + ... + done <file + + # Input from a program + # BAD + prog | while read line; do + ... + done + # GOOD + while read line; do + ... + done < <(prog) diff --git a/HACKING/code-style.md b/HACKING/code-style.md new file mode 100644 index 0000000..077bc49 --- /dev/null +++ b/HACKING/code-style.md @@ -0,0 +1,62 @@ +The style guidelines aren't terribly strict. As long as things are +consistent per-file, I'm pretty happy. + +Style guidelines +================ + +Unless you have a good reason, use `[[ ... ]]` instead of `[ ... ]`; +they work similarly, but `[[ ... ]]` is sometimes more readable (fine, +rarely, but never less), and is harder to make mistakes with quoting, +because it is syntactic magic, as opposed to `[ ... ]` which is an +executable which just happens to be implemented as a builtin. + +Use a litteral tab for indent. When indenting line-wrapped text, such +as that for `prose`, do it like this: (» indicates tab, · indicates +space) + + func() { + » prose "This is the first line. This paragraph is going to be + » ·······wrapped." + } + +The `; then` and `; do` should go on the same line as +`if`/`elif`/`for`/`while`. Also, there is no space before the `;`. + +Prefer the `for VAR in LIST` syntax over the `for ((init; cond; inc))` +syntax, when possible. For example (heh, `for` example): + + local i + for (( i = 1 ; i <= 10 ; i++ )); do + +should be + + local i + for i in {1..10}; do + +Of course, if the upper bound is a variable, the C-like syntax is +the better option, as otherwise you would have to use `seq` (calling +an external), or `eval` (gross, easy to mess up royally). + +Indent comments like you would code; don't leave them at the beginning +of the line. Example: + + for item in "${list[@]}"; do + if [[ $item == foo ]]; then + # BAD + foobar + fi + if [[ $item == bar ]]; then + # GOOD + barbaz + fi + done + +Fauno, I'm sorry. But I don't know how you can read your own code :P. + +Some people argue in favor of the useless use of cat, because data +should flow from left to right. However, the input redirection +doesn't have to go on the right side of a command: + + cat file | program # useless use of cat + program < file # data flows right to left + < file program # just right diff --git a/HACKING/contributing.md b/HACKING/contributing.md new file mode 100644 index 0000000..65066b5 --- /dev/null +++ b/HACKING/contributing.md @@ -0,0 +1,26 @@ +Contributing +============ + +I'd love to have your patches! Code should be hackable; if you want +to modify something, but can't figure out how: 1) ping me for help, 2) +it probably means the code was too complicated in the first place. + +Patches should be sent to <dev@lists.parabolagnulinux.org>; please put +"[PATCH]" and "libretools" in the subject line. If you have commit +access, but want me to look over it first, feel free to create a new +branch in git, and I will notice it. Try to avoid pushing to the +"master" branch unless it's a trivial change; it makes it easier to +review things; though I *will* look over every commit before I do a +release, so don't think you can sneak something in :) + +Be sure to make sure to follow the licensing requirements in +`HACKING/licensing.md` + +I'd love to discuss possible changes on IRC (I'm lukeshu), either on +irc.freenode.net#parabola or in personal messages. My account may be +online even if I'm not; I will eventually see your message, I do a +search for mentions of "luke" on #parabola every time I get on. + +Please write unit tests for new functionality. Or old functionality. +Please write unit tests! See `HACKING/testing.md` for details on +testing. diff --git a/HACKING/licensing.md b/HACKING/licensing.md new file mode 100644 index 0000000..6d17e31 --- /dev/null +++ b/HACKING/licensing.md @@ -0,0 +1,19 @@ +We don't require copyright assignment or any fancy paperwork! Just +make sure you specify the license and include a copyright statement +with your name and the current year. + +New code should (please) be licensed GPLv2+. I say v2 instead of v3 +because some code from Arch is GPLv2 (no "or any later version"), and +having to worry about which programs can be combined is a huge pain. + +Copyright statements should look like + + # Copyright (C) YEARS NAME <EMAIL> + +for most code, for 3rd-party code that has been imported, indent it a +bit: + + # Copyright (C) YEARS NAME <EMAIL> + +Always put a line with `# License:` specifying the license of that +file. diff --git a/HACKING/testing.md b/HACKING/testing.md new file mode 100644 index 0000000..8dee485 --- /dev/null +++ b/HACKING/testing.md @@ -0,0 +1,18 @@ +Testing +======= + +Please write unit tests for new things. Tests can be run with `make +check`, which just runs `./testenv roundup` in the `test/` directory. +Relatedly, you need the `roundup` (the `sh-roundup` package on +Parabola) tool to run the tests. `./testenv` can be given +`--no-network` and/or `--no-sudo` to dissable tests that require those +things. Make can be made to pass those things in by setting +`TESTENVFLAGS`. If you don't dissable either, I *strongly* recommend +setting TMPDIR to somewhere on a btrfs partition before running the +tests; otherwise the chroot tests will take forever. I mean, they +already take long on btrfs, but without it... _dang_. + +I also recommend having the `haveged` daemon running. That's good +general advice, but also: some of the tests make GPG keys, this +"should" take on the order of 1 second, but can take several minutes +if you don't have `haveged` running. @@ -0,0 +1,120 @@ +Installation of libretools is pretty straight-forward: + + $ make + # make install + +As a caveat, by default, if the directory `devtools-par` exists in the +same directory as the `libretools-${version}` directory, and contains +newer files than the libretools directory, they will be copied into +the libretools directory. See INSTALL-VCS for more details. + +Dependencies +------------ + +Unlike a lot of software, run-time dependencies are not required at +build-time. + +Libretools is mostly shell scripts. It very specifically targets +Parabola GNU/Linux-libre; there are a lot of dependencies, most of +which are undocumented. Switching away from a GNU user-land is bound +to cause issues. + +## Build dependencies + +The "unusual" build-time dependencies are: + + - GNU Make -- other `make`s will not work. + - GNU sed -- must support `-r` for ERE; BSD sed uses `-E` for this + purpose. + - Emacs 24.4+ -- `emacs --batch` is used to use Emacs Lisp to process + some text. GNU Emacs 24.4 introduced a new `advice-add` system + which we depend on. + - ronn -- A markdown-to-manpage converter + +At this time, the build system does not support not building the +documentation; ronn is required. + +Additionally, other usual "core utilities" are required: + + cat, chmod, cp, echo, false, find, install, ln, msguniq, rm, tr, + xgettext + +Also, `/bin/sh` must support `{brace,expansion}`. If it doesn't, find +a shell that does, and configure `make` to use it. Though, because +libretools is mostly shell scripts, if you deviate too much from a GNU +command-line environment, I suspect that you will run into more +issues. If I were targeting anything other than Parabola, this would +be a bigger issue. + +## Test suite dependencies + +If you wish to run the test suite, you will need the "roundup" shell +unit testing program. On Parabola GNU/Linux-libre it is called +"sh-roundup". If your operating system doesn't have it, it is +available at <http://bmizerany.github.io/roundup/>. + +## Run-time dependencies + +Being mostly shell scripts, many external program are used. Anything +that is included when installing the `base` package group on Parabola +GNU/Linux, I consider an implicit dependency. If something isn't used +now, that doesn't mean it won't be in the future. + +On top of that, the following dependencies are also needed: + +librelib subpackage: + - wget +gitget subpackage: + - librelib (provided) + - git +xbs subpackage: + - gitget (provided) + - librelib (provided) + - subversion (only for the 'abs' module) +main libretools subpackage: + - librelib (provided) + - xbs (provided) + - pacman 5.0 + - arch-install-scripts + - GNU Make (only needed for `librefetch`) + - ssh client (OpenSSH, only needed for `librerelease`) + - rsync + - systemd-nspawn (for the chroot tools) + - tokyocabinet + +Configuration +------------- + +This is not a GNU package, there is no `./configure` script. + +There are two ways to set configuration variables: + 1. Edit config.mk + 2. Pass `VARIABLE=VALUE` to `make` + +The configuration variables mostly match GNU packages, but default +values differ; libretools installs to `prefix=/usr` by default instead +of GNU's `prefix=/usr/local`. + +Building and installing subpackages +----------------------------------- + +There are several subpackages you can build and install. This is done +by running: + + $ make build-${package} + # make install-${package} + +respectively. In addition to `build` and `install`, the activities +you can do are: + + - `copy` -- copy necessary files from the devtools-par source code + - `build` -- build all programs and files + - `install` -- install everything + - `clean` -- remove generated files + +The subpackages you can run these on are: + + - libretools -- The main libretools package + - librelib -- Generic libraries included + - gitget -- A git downloader + - xbs -- Tools for dealing with differences between ABS/ABSLibre/PBS diff --git a/INSTALL-VCS b/INSTALL-VCS new file mode 100644 index 0000000..be80d41 --- /dev/null +++ b/INSTALL-VCS @@ -0,0 +1,17 @@ +The only tricky thing when building from Git is the weird dependence +on the devtools-par source code. + +The build system will by default look at `$(topdir)/../devtools-par` +for the devtools-par source code, and copy the relevant files into the +libretools directory. This can be changed by adjusting the +`devtoolsdir` configuration variable, see "Configuration" in the main +INSTALL file. If the `$(devtoolsdir)` directory does not exist, but +all of the copied files exist in the libretools directory, they will +simply be used. + +In the distribution tarball includes the copies of the devtools files, +so it is not necessary to download the devtools source separately +when using the tarball. + +Once you have the devtools source taken care of, you can build and +install like normal. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5be5195 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/config.mk +include $(topsrcdir)/automake.head.mk + +# these are the resulting packages +packages=libretools librelib gitget xbs +# and which directories they contain +libretools=\ + src \ + src/abslibre-tools \ + src/chroot-tools \ + src/devtools \ + src/librefetch \ + src/toru +librelib=src/lib +gitget=src/gitget +xbs=\ + src/xbs \ + src/xbs-abs \ + src/xbs-abslibre + +verbs=build install uninstall mostlyclean clean distclean maintainer-clean check +$(foreach verb,$(verbs),$(foreach package,$(packages),$(eval $(verb)-$(package): $(addsuffix /$(verb),$($(package)))))) +$(foreach verb,$(verbs),$(foreach package,$(packages),$(eval .PHONY: $(verb)-$(package)))) + +$(outdir)/check:: + cd $(@D)/test && ./testenv $(TESTENVFLAGS) roundup + +_po_rule = \ +po/%(package).pot: $(addsuffix /everything.pot,$(%(package))); \ + cat $^ | msguniq -Fi --to-code=UTF-8 > '$@' || rm -f '$@' +$(foreach package,$(packages),$(eval $(subst %(package),$(package),$(value _po_rule)))) + +pots = +am_out_files += $(foreach package,$(packages),po/$(package).pot) +am_clean_files += .var.* +am_gen_files += .srcversion-libretools.mk .srcversion-devtools.mk +am_subdirs = src $(foreach package,$(packages),$($(package))) + +include $(topsrcdir)/automake.tail.mk diff --git a/build-aux/Makefile.each.head/00-dist.mk b/build-aux/Makefile.each.head/00-dist.mk new file mode 100644 index 0000000..a094305 --- /dev/null +++ b/build-aux/Makefile.each.head/00-dist.mk @@ -0,0 +1,20 @@ +# Copyright (C) 2015-2016 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +ifeq ($(outdir),$(topoutdir)) +std.clean_files += $(addprefix $(dist.pkgname)-*,$(dist.exts) .tar /) +endif + +$(outdir)/dist: $(addprefix $(topoutdir)/$(dist.pkgname)-$(dist.version),$(dist.exts)) diff --git a/build-aux/Makefile.each.tail/10-std.mk b/build-aux/Makefile.each.tail/10-std.mk new file mode 100644 index 0000000..5150a71 --- /dev/null +++ b/build-aux/Makefile.each.tail/10-std.mk @@ -0,0 +1,46 @@ +# Copyright (C) 2015-2016 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# Add some more defaults to the *_files variables +std.clean_files += $(std.gen_files) $(std.cfg_files) $(std.out_files) + +# Fix each variable at its current value to avoid any weirdness +$(foreach c,src gen cfg out sys clean slow,$(eval std.$c_files := $$(std.$c_files))) + +# Make each of the standard variables relative to the correct directory +std.src_files := $(patsubst ./%,%,$(addprefix $(srcdir)/,$(std.src_files))) +std.gen_files := $(patsubst ./%,%,$(addprefix $(srcdir)/,$(std.gen_files))) +std.cfg_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.cfg_files))) +std.out_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.out_files))) +std.sys_files := $(addprefix $(DESTDIR),$(std.sys_files)) +std.clean_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.clean_files))) +std.slow_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.slow_files))) + +# Creative targets +$(outdir)/build : $(std.out_files) +$(outdir)/install : $(std.sys_files) +$(outdir)/installdirs: $(sort $(dir $(std.sys_files))) + +# Destructive targets +_std.uninstall/$(outdir) := $(std.sys_files) +_std.mostlyclean/$(outdir) := $(filter-out $(std.slow_files) $(std.cfg_files) $(std.gen_files) $(std.src_files),$(std.clean_files)) +_std.clean/$(outdir) := $(filter-out $(std.cfg_files) $(std.gen_files) $(std.src_files),$(std.clean_files)) +_std.distclean/$(outdir) := $(filter-out $(std.gen_files) $(std.src_files),$(std.clean_files)) +_std.maintainer-clean/$(outdir) := $(filter-out $(std.src_files),$(std.clean_files)) +$(addprefix $(outdir)/,uninstall mostlyclean clean distclean maintainer-clean): %: %-hook + $(RM) -- $(sort $(filter-out %/,$(_std.$(@F)/$(@D)))) + $(RM) -r -- $(sort $(filter %/,$(_std.$(@F)/$(@D)))) + $(RMDIR_P) $(sort $(dir $(_std.$(@F)/$(@D)))) 2>/dev/null || $(TRUE) +$(foreach t,uninstall mostlyclean clean distclean maintainer-clean, $(outdir)/$t-hook):: +.PHONY: $(foreach t,uninstall mostlyclean clean distclean maintainer-clean, $(outdir)/$t-hook) diff --git a/build-aux/Makefile.once.head/00-dist.mk b/build-aux/Makefile.once.head/00-dist.mk new file mode 100644 index 0000000..d5bfcd3 --- /dev/null +++ b/build-aux/Makefile.once.head/00-dist.mk @@ -0,0 +1,44 @@ +# Copyright (C) 2015-2016 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Developer configuration + +dist.exts ?= .tar.gz +dist.pkgname ?= $(PACKAGE) +dist.version ?= $(VERSION) + +ifeq ($(dist.pkgname),) +$(error dist.pkgname must be set) +endif +ifeq ($(dist.version),) +$(error dist.version must be set) +endif + +# User configuration + +CP ?= cp +GZIP ?= gzip +MKDIR ?= mkdir +MKDIR_P ?= mkdir -p +MV ?= mv +RM ?= rm -f +TAR ?= tar + +GZIPFLAGS ?= $(GZIP_ENV) +GZIP_ENV ?= --best + +# Implementation + +at.phony += dist diff --git a/build-aux/Makefile.once.head/10-std.mk b/build-aux/Makefile.once.head/10-std.mk new file mode 100644 index 0000000..3e058ec --- /dev/null +++ b/build-aux/Makefile.once.head/10-std.mk @@ -0,0 +1,39 @@ +# Copyright (C) 2015-2016 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Declare the default target +all: build +.PHONY: all noop + +# Standard creative PHONY targets +at.phony += build install installdirs +# Standard destructive PHONY targets +at.phony += uninstall mostlyclean clean distclean maintainer-clean + +at.dirlocal += std.src_files +at.dirlocal += std.gen_files +at.dirlocal += std.cfg_files +at.dirlocal += std.out_files +at.dirlocal += std.sys_files +at.dirlocal += std.clean_files +at.dirlocal += std.slow_files + +# User configuration + +DESTDIR ?= + +RM ?= rm -f +RMDIR_P ?= rmdir -p +TRUE ?= true diff --git a/build-aux/Makefile.once.tail/00-dist.mk b/build-aux/Makefile.once.tail/00-dist.mk new file mode 100644 index 0000000..b8b7733 --- /dev/null +++ b/build-aux/Makefile.once.tail/00-dist.mk @@ -0,0 +1,27 @@ +# Copyright (C) 2015-2016 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +_dist.copyfile = $(MKDIR_P) $(dir $2) && $(CP) -T $1 $2 +_dist.addfile = $(call _dist.copyfile,$3,$2/$(call at.relto,$1,$3)) +$(topoutdir)/$(dist.pkgname)-$(dist.version): $(foreach v,$(filter std.src_files/% std.gen_files/%,$(.VARIABLES)),$($v)) + $(RM) -r $@ $(@D)/tmp.$(@F) + $(MKDIR) $(@D)/tmp.$(@F) + $(foreach f,$^,$(call _dist.addfile,$(topsrcdir),$(@D)/tmp.$(@F),$f)$(at.nl)) + $(MV) $(@D)/tmp.$(@F) $@ || $(RM) -r $(@D)/tmp.$(@F) + +$(topoutdir)/$(dist.pkgname)-$(dist.version).tar: $(topoutdir)/$(dist.pkgname)-$(dist.version) + $(TAR) cf $@ -C $(<D) $(<F) +$(topoutdir)/$(dist.pkgname)-$(dist.version).tar.gz: $(topoutdir)/$(dist.pkgname)-$(dist.version).tar + $(GZIP) $(GZIPFLAGS) < $< > $@ diff --git a/common.each.head.mk b/common.each.head.mk new file mode 100644 index 0000000..e3a269e --- /dev/null +++ b/common.each.head.mk @@ -0,0 +1,35 @@ +pkgconfdir = $(sysconfdir)/libretools.d +pkgdocdir = $(docdir)/libretools +pkglibexecdir = $(libexecdir)/libretools + +# Detect things about the directory we're in #################################### + +srcfiles-ignore/$(srcdir) = $(addsuffix /%,$(_am_subdirs/$(@D))) + +ifeq ($(wildcard $(topsrcdir)/.git),) +include $(srcdir)/.srcfiles.mk +else +$(srcdir)/.srcfiles.mk: FORCE + @echo srcfiles = $(filter-out $(srcfiles-ignore/$(@D)),$(shell cd $(@D) && git ls-files)) | $(write-ifchanged) $@ +-include $(srcdir)/.srcfiles.mk +endif + +detect-ignore = +detect-ignore-exec = +detect-ignore-conf = +detect-ignore-ronn = +detect-ignore-md = HACKING.md +detect-ignore-sh = + +devtools-files = + +libretools-srcs = $(detect-all) +libretools-mans = $(patsubst %.ronn,%,$(detect-ronn)) +libretools-bins = $(detect-exec) +libretools-libexecs = +libretools-libs = $(detect-sh) +libretools-docs = $(detect-md) $(detect-ronn) +libretools-confs = $(detect-conf) + +libretools-files = $(libretools-mans) $(libretools-bins) $(libretools-libexecs) $(libretools-libs) $(libretools-docs) $(libretools-confs) +pots = $(libretools-bins) $(libretools-libexecs) $(libretools-libs) diff --git a/common.each.tail.mk b/common.each.tail.mk new file mode 100644 index 0000000..e58b5c5 --- /dev/null +++ b/common.each.tail.mk @@ -0,0 +1,89 @@ +# Copyright (C) 2015, 2016 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +ifneq ($(sort $(wildcard $(addprefix $(srcdir)/,$(devtools-files)))),$(sort $(addprefix $(srcdir)/,$(devtools-files)))) +ifeq ($(wildcard $(devtoolsdir)/),) +$(error config.mk:devtoolsdir points to a non-existant directory: $(devtoolsdir)) +endif +endif + +am_src_files += $(libretools-srcs) +am_gen_files += .srcfiles.mk $(devtools-files) +am_out_files += $(filter-out $(am_src_files) $(am_gen_files),$(libretools-files)) \ + $(if $(pots),everything.pot) +am_sys_files += $(addprefix $(bindir)/,$(libretools-bins)) \ + $(addprefix $(pkgconfdir)/,$(libretools-confs)) \ + $(addprefix $(pkglibexecdir)/,$(libretools-libexecs) $(libretools-libs)) \ + $(addprefix $(pkgdocdir)/,$(libretools-docs)) \ + $(addprefix $(mandir)/man1/,$(filter %.1,$(libretools-mans))) \ + $(addprefix $(mandir)/man2/,$(filter %.2,$(libretools-mans))) \ + $(addprefix $(mandir)/man3/,$(filter %.3,$(libretools-mans))) \ + $(addprefix $(mandir)/man4/,$(filter %.4,$(libretools-mans))) \ + $(addprefix $(mandir)/man5/,$(filter %.5,$(libretools-mans))) \ + $(addprefix $(mandir)/man6/,$(filter %.6,$(libretools-mans))) \ + $(addprefix $(mandir)/man7/,$(filter %.7,$(libretools-mans))) \ + $(addprefix $(mandir)/man8/,$(filter %.8,$(libretools-mans))) +am_clean_files += *.pot *.ugly *.rej *.orig .tmp* + +exec_$(outdir) := $(exec_$(outdir)) $(libretools-bins) $(libretools-libexecs) + +_is_executable = $(filter $(exec_$(@D)),$(@F)) + +$(srcdir)/%.in: $(devtoolsdir)/%.in + cp -T '$<' '$@' +$(srcdir)/%.in: $(devtoolsdir)/lib/% + cp -T '$<' '$@' +$(outdir)/%: $(srcdir)/%.in + @echo 'EDIT < $< > $@'; $(edit) < '$<' | install -T -m$(if $(_is_executable),755,644) /dev/stdin '$@' +$(outdir)/%: $(srcdir)/%.ronn + ronn --roff $(RONNFLAGS) < '$<' > '$@' +$(outdir)/%.html: $(srcdir)/%.ronn + ronn --html $(RONNFLAGS) < '$<' > '$@' +$(outdir)/%.pot: $(outdir)/% $(topsrcdir)/src/lib/librexgettext + $(topsrcdir)/src/lib/librexgettext $(LIBREXGETTEXT_FLAGS) '$<' > '$@' +$(outdir)/%.pot: $(srcdir)/% $(topsrcdir)/src/lib/librexgettext + $(topsrcdir)/src/lib/librexgettext $(LIBREXGETTEXT_FLAGS) '$<' > '$@' +$(outdir)/everything.pot: $(addprefix $(outdir)/,$(addsuffix .pot,$(pots))) + cat $^ | $(pofmt) > '$@' + +# If we have a .patch file, the flow is: +# $(devtoolsdir)/%.in -> %.in + %.patch -> %.ugly -> % +_do_patch = $(filter $(patsubst %.patch,%,$(filter %.patch,$(detect-all))),$(patsubst %.in,%,$(devtools-files))) +$(outdir)/%.ugly: $(srcdir)/%.in $(srcdir)/%.patch + cp -T $< $@ + patch $@ $(<D)/$*.patch +$(sort $(addprefix $(outdir)/,$(_do_patch))): $(outdir)/%: $(outdir)/%.ugly + @echo 'EDIT < $< > $@'; $(edit) < '$<' | install -T -m$(if $(_is_executable),755,644) /dev/stdin '$@' + @echo 'INDENT $@'; $(call indent,$@) + +$(DESTDIR)$(pkgconfdir)/% : $(outdir)/% ; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(pkgdocdir)/% : $(outdir)/% ; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man1/%.1: $(outdir)/%.1; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man2/%.2: $(outdir)/%.2; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man3/%.3: $(outdir)/%.3; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man4/%.4: $(outdir)/%.4; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man5/%.5: $(outdir)/%.5; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man6/%.6: $(outdir)/%.6; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man7/%.7: $(outdir)/%.7; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(mandir)/man8/%.8: $(outdir)/%.8; install -T -Dm644 '$<' '$@' +$(DESTDIR)$(pkglibexecdir)/%: $(outdir)/% ; mkdir -p '$(@D)' && cp -T '$<' '$@' +$(DESTDIR)$(bindir)/% : $(outdir)/% ; install -T -Dm755 '$<' '$@' +# Repeat the last two rules again with explicit targets because +# otherwise it would try to do src/xbs->/bin/xbs instead of +# src/xbs/xbs->/bin/xbs +ifneq ($(filter $(notdir $(outdir)),$(libretools-files)),) +$(DESTDIR)$(pkglibexecdir)/$(notdir $(outdir)): $(DESTDIR)$(pkglibexecdir)/%: $(outdir)/% ; mkdir -p '$(@D)' && cp -T '$<' '$@' +$(DESTDIR)$(bindir)/$(notdir $(outdir)): $(DESTDIR)$(bindir)/% : $(outdir)/% ; install -T -Dm755 '$<' '$@' +endif diff --git a/common.once.head.mk b/common.once.head.mk new file mode 100644 index 0000000..33084fa --- /dev/null +++ b/common.once.head.mk @@ -0,0 +1,83 @@ +# Copyright (C) 2015 Luke Shumaker +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Configure how Make works +MAKEFLAGS += --no-builtin-rules --no-builtin-variables +.SECONDARY: +.DELETE_ON_ERROR: + +.PHONY: FORCE + +write-ifchanged = $(topsrcdir)/write-ifchanged + +# Magic for tracking variables that affect files. If a file changes +# based on a variable, just stick $(var)VARNAME as a dependency. +var = $(call _am_path,$(topoutdir)/.var.) +$(var)%: FORCE + @printf '%s' '$($*)' | $(write-ifchanged) $@ + +# Usage: <INPUT $(edit) >OUTPUT +# Expand m4_include macros to use librelib +# This is used when using sources grabbed from devtools +# Magic for doing @variable@ replacement in files +edit = sed \ + -e 's|^\#!\s*/bin/bash|\#!/usr/bin/env bash|' \ + -e 's|m4_include(lib/\(.*\))|. "$$(librelib \1)"|' \ + $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$(v)@|$($(v))|g' ) + +# Usage: $(call indent,FILENAME) +# Command to auto-indent a file. +indent = emacs --batch $1 \ + --eval '(setq make-backup-files nil)' \ + --eval '(setq sh-basic-offset 8)' \ + --eval '(defun sh-smie-sh-rules--fix (args) "fix bug in Emacs 24 sh-script.el" (if (equal args (list :after "then")) (list :after "if") args))' \ + --eval "(advice-add 'sh-smie-sh-rules :filter-args \#'sh-smie-sh-rules--fix)" \ + --eval '(indent-region (point-min) (point-max) nil)' \ + -f save-buffer &>/dev/null + +# Usage <INPUT $(pofmt) >OUTPUT +# Normalize a .po(t) file +pofmt = msguniq -Fi --to-code=UTF-8 + +# It's easy to think of these as "each" variables, but because they +# will be evaluated on demand, only am_src_files needs to be "each". +detect-all = $(filter-out $(detect-ignore) ,$(srcfiles)) +detect-exec = $(filter-out $(detect-ignore-exec),$(patsubst $(srcdir)/%,%,$(shell find $(addprefix $(srcdir)/,$(detect-all)) -executable))) +detect-conf = $(filter-out $(detect-ignore-conf),$(filter %.conf ,$(detect-all))) +detect-ronn = $(filter-out $(detect-ignore-ronn),$(filter %.ronn ,$(detect-all))) +detect-md = $(filter-out $(detect-ignore-md) ,$(filter %.ronn %.md,$(detect-all))) +detect-sh = $(filter-out $(detect-ignore-sh) ,$(filter %.sh ,$(detect-all))) + +ifeq ($(wildcard $(topsrcdir)/.git),) +include $(topsrcdir)/.srcversion-libretools.mk +else +$(topsrcdir)/.srcversion-libretools.mk: FORCE + @{ \ + echo LIBRETOOLS_VERSION = $(patsubst v%,%,$(shell cd $(topsrcdir) && git describe --tags)); \ + echo LIBRETOOLS_COMMIT = $(shell cd $(topsrcdir) && git rev-parse HEAD); \ + :; } | $(write-ifchanged) $@ +-include $(topsrcdir)/.srcversion-libretools.mk +endif + +ifeq ($(wildcard $(devtoolsdir)/.git),) +include $(topsrcdir)/.srcversion-devtools.mk +else +$(topsrcdir)/.srcversion-devtools.mk: FORCE + @{ \ + echo DEVTOOLS_VERSION = $(patsubst libretools-%,%,$(shell cd $(devtoolsdir) && git describe --tags)); \ + echo DEVTOOLS_COMMIT = $(shell cd $(devtoolsdir) && git rev-parse HEAD); \ + :; } | $(write-ifchanged) $@ +-include $(topsrcdir)/.srcversion-devtools.mk +endif diff --git a/common.once.tail.mk b/common.once.tail.mk new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/common.once.tail.mk diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..414f761 --- /dev/null +++ b/config.mk @@ -0,0 +1,30 @@ +# Note: In the default version of this file, commented out values +# indicate what the GNU standards dictate, when our values +# differ. We're not a GNU package. + +ifeq ($(topsrcdir),) +topsrcdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) +endif + +PACKAGE = libretools +VERSION = $(LIBRETOOLS_VERSION) + +DESTDIR = + +#prefix = /usr/local +prefix = /usr +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +#libexecdir = $(exec_prefix)/libexec +libexecdir = $(exec_prefix)/lib + +datarootdir = $(prefix)/share +datadir = $(datarootdir) +#sysconfdir = $(prefix)/etc +sysconfdir = /etc + +docdir = $(datarootdir)/doc +mandir = $(datarootdir)/man + +devtoolsdir = $(call abspath,$(topsrcdir)/../devtools-par) +RONNFLAGS = --manual='libretools Manual' --organization='Parabola' diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000..e3a8c5d --- /dev/null +++ b/po/.gitignore @@ -0,0 +1 @@ +*.pot diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e1e5cf4 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,7 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../config.mk +include $(topsrcdir)/automake.head.mk +pkgconfdir = $(sysconfdir) + +srcfiles-ignore/$(srcdir) = $(addsuffix /%,abslibre-tools chroot-tools devtools gitget lib librefetch toru xbs xbs-abs xbs-abslibre) + +include $(topsrcdir)/automake.tail.mk diff --git a/src/abslibre-tools/Makefile b/src/abslibre-tools/Makefile new file mode 100644 index 0000000..2903f4a --- /dev/null +++ b/src/abslibre-tools/Makefile @@ -0,0 +1,4 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +include $(topsrcdir)/automake.tail.mk diff --git a/src/abslibre-tools/createworkdir b/src/abslibre-tools/createworkdir new file mode 100755 index 0000000..5599de9 --- /dev/null +++ b/src/abslibre-tools/createworkdir @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# CreateWorkDir +# Creates a dir structure for working with Parabola packages + +# Copyright (C) 2010-2011 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +set -euE + +. libremessages +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools WORKDIR ABSLIBRERECV ABSLIBRESEND # these are asked for by `xbs download` + +trap 'error "Aborting..."' EXIT + +msg "Creating WORKDIR at %s..." "$WORKDIR" +mkdir -p "$WORKDIR" + +msg "Creating staging directory in WORKDIR..." +mkdir -p "$WORKDIR/staging" + +cmd=(xbs -b abslibre download) +if ! "${cmd[@]}"; then + error "Could not clone ABSLibre" + plain "Try running this command:" + echo + printf '%q ' "${cmd[@]}" + echo + exit 1 +fi + +msg "Finished, your packaging directory tree looks like this now:" +ls --color=auto "${WORKDIR}"/* + +trap -- EXIT diff --git a/src/abslibre-tools/createworkdir.md b/src/abslibre-tools/createworkdir.md new file mode 100644 index 0000000..e50b00f --- /dev/null +++ b/src/abslibre-tools/createworkdir.md @@ -0,0 +1,36 @@ +# CreateWorkDir + +This script recreates a proper directory tree for packaging. Its aim is to help +you be organized with the work you do as a packager, and establish a certain +standard for packages' publication, so you don't have to lose much time with +them. Just package and upload! + +It will create a directory tree like this: + + $WORKDIR/ + ├── abslibre/ + │ ├── .git/ + │ ├── libre/<PKGBUILDS> + │ └── libre-testing/<PKGBUILDS> + └── staging/ + ├── libre/ + └── libre-testing/ + +*Related Variables* + - WORKDIR + +## staging/ + +This directory contains one directory for each repository, where the resulting +packages are in moved for syncing against the main repository on Parabola's +server. This directory is architecture independent. + +## abslibre/ + +This is the git repo for Parabola's PKGBUILDs. Here you can find the ABS tree +for our packages, and also where you'll have to put new ones for commit. + +(You'll need push access to Parabola's main server, but pulling is public.) + +*Related Variables* + - ABSLIBREGIT diff --git a/src/abslibre-tools/diff-unfree b/src/abslibre-tools/diff-unfree new file mode 100755 index 0000000..12c919f --- /dev/null +++ b/src/abslibre-tools/diff-unfree @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# This script will help you diff a *-libre PKGBUILD against the unfree one +# to check for updates. + +# Copyright (C) 2010-2011 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools DIFFPROG || exit 1 + +usage() { + print "Usage: %s [community|packages] [unfree-package] [repo]" "${0##*/}" + print "Usage: %s --help" "${0##*/}" + prose "Helps you diff build scripts from ABSLibre against (Unfree) ABS." + echo + prose "Package name and repo will we guessed if you don't specify them." +} + +main() { + if [[ "$1" == "--help" ]]; then + usage + exit 0 + fi + + local package_guess=${PWD##*/} + local repo=${1:-$(basename ${PWD%/*})} + local package=${2:-${package_guess%-libre}} + local trunk=${3:-trunk} + + svnrepo="packages" + case $repo in + community*) svnrepo="community";; + multilib*) svnrepo="community";; + *) :;; + esac + + if [[ ! -r PKGBUILD ]]; then + error "This is not a build dir." + exit 1 + fi + + + tmp_dir="$(mktemp --tmpdir -d ${package}.XXXXXX)" + if [[ ! -d "${tmp_dir}" ]]; then + error "Can't create temp dir" + exit 1 + fi + unfree_dir="${tmp_dir}/${svnrepo}/${package}/${trunk}" + + pushd "${tmp_dir}" &>/dev/null + + msg "Getting diff from %s..." "$repo/$package" + + svn checkout --depth=empty svn://svn.archlinux.org/$svnrepo &>/dev/null + + cd ${svnrepo} + svn update ${package} + + # Back to start dir + popd &>/dev/null + + msg "Diffing files" + + for _file in ${unfree_dir}/*; do + msg2 "%s" "$(basename "${_file}")" + ${DIFFPROG} "$PWD/$(basename "${_file}")" "${_file}" + done +} + +main "$@" diff --git a/src/abslibre-tools/libreaddiff b/src/abslibre-tools/libreaddiff new file mode 100755 index 0000000..8698920 --- /dev/null +++ b/src/abslibre-tools/libreaddiff @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- + +# Copyright (C) 2011-2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012 Daniel Molina (lluvia) +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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 + +. libremessages +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools WORKDIR + +for arg in "$@" ; do + case "$arg" in + -h|--h|--he|--hel|--help|-\?) + { + print 'Usage: %s repo [arch]' "${0##*/}" + echo + prose "This script outputs a diff of package names and versions + in repo between pacman's sync db and abslibre checkout." + } >&2 + exit 0 + ;; + esac +done + +# The repo to find missing packages in. +repo=$1 +# The arch to check in Arch repos, other will have all arches checked. +arch=${2:-mips64el} +# A Python tuple of repos which don't have arch=any packages. +archrepos='("core", "extra", "community")' + +diff -U0 \ + <( ( + cd /var/lib/pacman/sync + for f in $repo.db ; do + tar xOf $f | python -c 'import sys +arch = None +name = None +version = None +it = iter(sys.stdin) +try: + while True: + line = next(it) + if line == "%ARCH%\n": + arch = next(it) + if arch == "'"$arch"'\n" or "'$repo'" not in '"$archrepos"': + print("%s-%s" % (name.strip(), version.strip())) + if line == "%NAME%\n": + name = next(it) + if line == "%VERSION%\n": + version = next(it) +except StopIteration: + pass +' + done + ) | sort ) \ + <( ( + cd "${WORKDIR}/abslibre" + # Needed to not include pkgnames specific to other arches. + CARCH=$arch + for f in $repo/* ; do + load_PKGBUILD "$f/PKGBUILD" || continue + is_here=false + for arc in ${arch[@]} ; do + if [[ "$arc" == "any" ]] || [[ "$arc" == "$CARCH" ]] ; then + is_here=true + break + fi + done + if [[ "$is_here" == "true" ]] ; then + for name in "${pkgname[@]}" ; do + if [[ -z "$epoch" ]] ; then + echo $name-$pkgver-$pkgrel + else + echo $name-$epoch:$pkgver-$pkgrel + fi + done + fi + done + ) | sort ) | sed -rn 's/^[+-][^+-].+$/&/p' diff --git a/src/abslibre-tools/libredbdiff b/src/abslibre-tools/libredbdiff new file mode 100755 index 0000000..09c7ad1 --- /dev/null +++ b/src/abslibre-tools/libredbdiff @@ -0,0 +1,360 @@ +#!/usr/bin/env bash +name="Libredbdiff" + +# Copyright (C) 2014 Esteban Carnevale <alfplayer@mailoo.org> +# Copyright (C) 2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This program 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/>. + +baseconfpath="/etc/libredbdiff" +basedbpath="/var/lib/libredbdiff" + +conffile="$baseconfpath/pacman.conf.parabola" +conffilearch="$baseconfpath/pacman.conf.archlinux" + +dbpath="$basedbpath/pacman.parabola" +dbpatharch="$basedbpath/pacman.archlinux" + +mirrorlist="$baseconfpath/mirrorlist.parabola" +mirrorlistarch="$baseconfpath/mirrorlist.archlinux" + +mirror='http://repo.parabola.nu/$repo/os/$arch' +mirrorarch='http://mirrors.kernel.org/archlinux/$repo/os/$arch' + +repos="libre pcr libre-multilib nonprism" + +field_pkgname_parabola=30 +field_pkgname_arch=30 + + +. libremessages + +cmd="${0##*/}" + +arch_packages_tmp="/tmp/${cmd}.arch-packages" +parabola_packages_tmp="/tmp/${cmd}.parabola-packages" + +field_pkgname_total="$((${field_pkgname_parabola} + ${field_pkgname_arch}))" +printf_format="%s %-${field_pkgname_parabola}s%-${field_pkgname_arch}s %s | %s\n" +printf_format_noarch="%s %-${field_pkgname_total}s %s\n" + +downloadfile() { + local outfile=$1 + local url=$2 + local mesg=("${@:3}") + if [[ ! -e $outfile ]] ; then + msg "${mesg[@]}" + if wget -q "$url" -O "$outfile"; then + return 255 + else + die "Failed to download %q. Exiting." "$outfile" + fi + elif [[ $init ]]; then + warning "%q already exists. Skipping." "$outfile" + fi +} + +enablerepo() { + repo="$1" + conffile_arg="$2" + msg2 "Enabling repo %q in %q" "$repo" "${conffile_arg}" + sed -i "s/\#\[$repo\]/[$repo]/" "${conffile_arg}" + sed -i "\/\[$repo\]/,+1 s/#Include/Include/" "${conffile_arg}" +} + +createdir() { + local dir=$1 + if [[ ! -e $dir ]] ; then + msg "Creating directory %q" "$dir" + mkdir -- "$1" || die "Failed to create directory %q. Exiting." "$dir" + elif [[ $init ]]; then + warning "%q already exists. Skipping." "$dir" + fi +} + +setmirror() { + local distro="$1" + local mirror="$2" + local mirrorlist="$3" + if [[ $init ]] && [[ $mirror ]]; then + mirrorescaped="${mirror//./\\.}" + mirrorescaped="${mirrorescaped//\$/\\$}" + msg2 "Setting %s as the only enabled %s mirror." "${mirror}" "${distro}" + sed -i 's|^#\(Server = '"${mirrorescaped}"'\)$|\1|' "${mirrorlist}" + fi +} + +filenotfound() { + local file=$1 + if [[ ! -r $1 ]]; then + error "Could not read %q." "$file" + die "Nothing done. It may be necessary to run %q without \ +arguments as root to initialize %s." "$cmd" "$name" + fi +} + +initialize() { + createdir "$baseconfpath" + createdir "$basedbpath" + + downloadfile \ + "${conffile}" \ + "https://projects.parabola.nu/abslibre.git/plain/libre/pacman/pacman.conf.x86_64" \ + "Downloading Parabola %q" \ + pacman.conf + if [[ $? == 255 ]] ; then + msg2 "Setting DBPath in %q" "${conffile}" + sed -i "s|^#DBPath .*|DBPath = ${dbpath}|" "${conffile}" + enablerepo nonprism "${conffile}" + enablerepo pcr "${conffile}" + enablerepo libre-multilib "${conffile}" + enablerepo multilib "${conffile}" + fi + + downloadfile \ + "${conffilearch}" \ + "https://projects.archlinux.org/svntogit/packages.git/plain/pacman/trunk/pacman.conf.x86_64" \ + "Downloading Arch %q" \ + pacman.conf + if [[ $? == 255 ]] ; then + msg2 "Setting DBPath in %q" "${conffilearch}" + sed -i "s|^#DBPath .*|DBPath = ${dbpatharch}|" "${conffilearch}" + msg2 "Setting Arch mirrorlist file in %q" "${conffilearch}" + sed -i "s|/etc/pacman\.d/mirrorlist$|$baseconfpath/mirrorlist.archlinux|" \ + "${conffilearch}" + enablerepo multilib "${conffilearch}" + fi + + downloadfile \ + "${mirrorlist}" \ + "https://repo.parabola.nu/mirrorlist.txt" \ + "Downloading Parabola %q" \ + mirrorlist + if [[ $? == 255 ]] ; then + sed -i 's|^Server|#Server|' "${mirrorlist}" + setmirror "Parabola" "$mirror" "$mirrorlist" + fi + + downloadfile \ + "${mirrorlistarch}" \ + "https://projects.archlinux.org/svntogit/packages.git/plain/pacman-mirrorlist/trunk/mirrorlist" \ + "Downloading Arch %q" \ + mirrorlist + if [[ $? == 255 ]] ; then + setmirror "Arch" "$mirrorarch" "$mirrorlistarch" + fi +} + +repo_test() { + for repo in ${repos} ; do + if [[ $repo == $1 ]] ; then + found=1 + return 0 + fi + done + if [[ $found != 1 ]] ; then + die "The specified Parabola repo \"%s\" cannot be compared. It's not in the list of repos in the configuration variable \"repos\"." "$1" + fi +} + +compare_pkgs() { + local cmp + if [[ ${verarch[$pkgname]} ]] ; then + cmp=$(vercmp "${ver[$pkgname]}" "${verarch[$pkgname]}") + if [[ $cmp -lt 0 ]]; then + printf "${printf_format}" \ + '=' \ + "${pkgname}" \ + "" \ + "${ver[$pkgname]}" \ + "${verarch[$pkgname]}" + fi + elif [[ ${provides[$pkgname]} ]]; then + for provide in "${provides[$pkgname]}"; do + if [[ ${verarch["$provide"]} ]]; then + cmp=$(vercmp "${ver[$pkgname]}" "${verarch[$provide]}") + if [[ $cmp -lt 0 ]]; then + printf "${printf_format}" \ + 'p' \ + "${pkgname}" \ + "${provide}" \ + "${ver[$pkgname]}" \ + "${verarch[$provide]}" + fi + fi + done + else + printf "${printf_format_noarch}" \ + 'o' \ + "${pkgname}" \ + "${ver[$pkgname]}" + fi +} + +print_cmp() { + local repo="$1" + awk -F/ -v repo="$repo" \ + '$1 == repo {print $2}' \ + ${parabola_packages_tmp} | \ + while read -a line ; do + ver["${line[0]}"]="${line[1]}" + provides[${line[0]}]="${line[@]:2}" + pkgname=${line[0]} + compare_pkgs + done +} + +usage() { + print "Usage: %q [-n|-h]" "$cmd" + print 'Show packages that need to be updated from Arch repositories.' + echo + prose "Compares packages in Parabola repositories. Packages from + all configured Parabola repositories are compared. A Parabola + repository name can be specified as argument to compare only + packages in that repository." + echo + prose "The default mode of operation is to download/update all necessary + files for comparison, but not compare them. Specify the \`-n\` + flag to not download anything, but to compare already downloaded + files." + echo + print 'Options:' + flag '-n' "Don't update anything, just compare already downloaded files." + flag '-h' 'Show this message' + echo + print "Output format:" + print "type_character parabola_pkgname (arch_pkgname) parabola_pkgver - (arch_pkgver)" + echo + print "Type characters:" + flag '=' "Arch package with the same pkgname and greater pkgver was found" + flag 'p' "Arch package with pkgname equal to a provide and greater + pkgver was found In this case arch_pkgname is a provide of + parabola_pkgname" + flag 'o' "No Arch package with the same pkgname or with pkgname equal to + a provide was found" +} + +main() { + local UPDATE=1 + local arg + local repo_arg + + for arg in "$@"; do + case "$arg" in + -n|--noupdate) + UPDATE=0 + ;; + -h|--help) + usage + return 0 + ;; + *) + repo_test "$arg" + repo_arg="$arg" + break + ;; + esac + done + + if (( $UPDATE )) ; then + if [[ $EUID != 0 ]]; then + die "To initialize %s or update %s pacman databases, %s must be run as root (without arguments). Nothing done." \ + "$name" \ + "$name" \ + "$cmd" + fi + + if ! [[ -e "${conffile}" && \ + -e "${conffilearch}" && \ + -e "${mirrorlist}" && \ + -e "${mirrorlist}" ]]; then + warning "At least one %s configuration file is missing." \ + "${name}" + msg "Downloading and preparing missing configuration files." + init=1 + fi + + createdir "$baseconfpath" + createdir "$basedbpath" + + initialize + + if ! [[ -d "$dbpath" && \ + -d "$dbpatharch" ]]; then + warning "At least one %s pacman DB directory is missing. Synchronizing %s DB files." \ + "${name}" "${name}" + fi + + createdir "$dbpath" + msg "Synchronizing %s pacman databases for Parabola" "$name" + pacman --config "${conffile}" -Sy || + die "Failed to synchronize pacman database for Parabola. Exiting." + + createdir "$dbpatharch" + msg "Synchronizing %s pacman databases for Arch" "$name" + pacman --config "${conffilearch}" -b "${dbpatharch}" -Sy || + die "Failed to synchronize pacman database for Arch. Exiting." + + msg "%s pacman databases are updated. %s is ready. Run %q -n to print results." \ + "$name" "$name" "$cmd" + return 0 + else + filenotfound "${dbpath}" + filenotfound "${dbpatharch}" + filenotfound "${conffile}" + filenotfound "${conffilearch}" + filenotfound "${mirrorlist}" + filenotfound "${mirrorlistarch}" + + unset provides ver verarch + declare -gA provides ver verarch + + if ! [[ -d "$dbpath" && \ + -d "$dbpatharch" ]]; then + die "At least one %s pacman DB directory is missing. To update %s pacman databases, %s must be run as root. Nothing done." \ + "$name" \ + "$name" \ + "$cmd" + fi + + pacman --config "${conffilearch}" -Ss | \ + grep -v '^ ' | \ + awk -F/ '{print $2}' \ + > ${arch_packages_tmp} || \ + die "pacman command to get Arch package data has failed. Exiting." + chmod 777 ${arch_packages_tmp} + + while read -a line; do + verarch["${line[0]}"]="${line[1]}" + done < ${arch_packages_tmp} + + expac --config "${conffile}" -S '%r/%n %v %S' \ + > ${parabola_packages_tmp} || \ + die "expac command to get Parabola package data has failed. Exiting." + chmod 777 ${parabola_packages_tmp} + + if [[ ${repo_arg} ]] ; then + print_cmp "${repo_arg}" + else + for repo in ${repos} ; do + echo "[$repo]" + print_cmp "$repo" + done + fi + fi +} + +main "$@" diff --git a/src/abslibre-tools/librerelease b/src/abslibre-tools/librerelease new file mode 100755 index 0000000..b7a77d3 --- /dev/null +++ b/src/abslibre-tools/librerelease @@ -0,0 +1,257 @@ +#!/usr/bin/env bash +# Librerelease +# Uploads packages and releases them + +# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2010-2013 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2013 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For just the create_signature() function: +# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2005 Aurelien Foret <orelien@chez.com> +# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +# create_signature() is taken from pacman:makepkg, which is GPLv2+, +# so we take the '+' to combine it with our GPLv3+. + +. libremessages +. "$(librelib conf.sh)" + +dryrun="" +upload_only=false +readonly rsync_flags=( + --no-group + --no-perms + --copy-links + --hard-links + --partial + --human-readable + --progress + -e ssh +) + +# Functions #################################################################### + +list0_files() { + find -L "${WORKDIR}/staging" -type f -not -name '*.lock' \ + -exec realpath -z --relative-to="${WORKDIR}/staging" {} + +} + +# This function is taken almost verbatim from makepkg +create_signature() { + local ret=0 + local filename="$1" + msg "Signing package..." + + local SIGNWITHKEY=() + if [[ -n $GPGKEY ]]; then + SIGNWITHKEY=(-u "${GPGKEY}") + fi + # The signature will be generated directly in ascii-friendly format + gpg --detach-sign --use-agent "${SIGNWITHKEY[@]}" "$filename" || ret=$? + + + if (( ! ret )); then + msg2 "Created signature file %s." "$filename.sig" + else + error "Failed to sign package file." + return $ret + fi +} + +sign_packages() { + IFS=$'\n' + local files=($(find "${WORKDIR}/staging/" \ + \( -type d -name "${ABSLIBREDEST##*/}" \) -prune \ + -o \( -type f -not -iname '*.sig' \) -print)) + local file + for file in "${files[@]}"; do + if [[ -f "${file}.sig" ]]; then + msg2 "File signature found, verifying..." + + # Verify that the signature is correct, else remove for re-signing + if ! gpg --quiet --verify "${file}.sig" >/dev/null 2>&1; then + error "Failed! Re-signing..." + rm -f "${file}.sig" + fi + fi + + if ! [[ -f "${file}.sig" ]]; then + create_signature "$file" || return 2 + fi + done +} + +# Clean everything if not in dry-run mode +clean_files() { + local file_list=$1 + + local rmcmd=(rm -fv) + if [[ -n "${dryrun}" ]]; then + rmcmd=(printf "$(_ "removed '%s' (dry-run)")\n") + fi + + msg "Removing files from local staging directory" + cd "${WORKDIR}/staging" && xargs -0r -a "$file_list" "${rmcmd[@]}" + cd "${WORKDIR}/staging" && find . -mindepth 1 -type d -empty \ + -exec rmdir -p {} + 2>/dev/null +} + +################################################################################ + +usage() { + print "Usage: %s [OPTIONS]" "${0##*/}" + echo + prose 'This script uploads packages on $WORKDIR/staging + to the Parabola server.' + echo + print "Options:" + flag '-c' 'Clean; delete packages in $WORKDIR/staging' + flag '-l' "List; list packages but not upload them" + flag '-u' "Upload-only; do not run db-update on the server" + + flag '-n' "Dry-run; don't actually do anything" + flag '-h' "Show this message" +} + +main() { + if [[ -w / ]]; then + error "This program should be run as regular user" + return 1 + fi + + # Parse options + local mode="release_packages" + while getopts 'clunh' arg; do + case $arg in + c) mode=clean ;; + l) mode=pretty_print_packages ;; + u) upload_only=true ;; + n) dryrun="--dry-run" ;; + h) mode=usage ;; + *) usage >&2; return 1 ;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# != 0 ]]; then + usage >&2 + return 1 + fi + + if [[ $mode == usage ]]; then + usage + return 0 + fi + + load_files makepkg + check_vars makepkg GPGKEY + load_files libretools + check_vars libretools WORKDIR REPODEST ABSLIBREDEST || return 1 + REPODEST+='/staging/' + # The following settings are actually optional + #check_vars libretools HOOKPRERELEASE HOOKPOSTRELEASE || return 1 + + "$mode" +} + +# The different modes (sans 'usage') ########################################### + +pretty_print_packages() { + find "$WORKDIR/staging/" -mindepth 1 -maxdepth 1 -type d -not -empty | sort | + while read -r path; do + msg2 "${path##*/}" + cd "$path" + find -L . -type f -not -name '*.lock' | sed 's|^\./| |' | sort + done +} + +clean() { + lock 8 "${WORKDIR}/staging.lock" \ + 'Waiting for an exclusive lock on the staging directory' + + local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)" + trap "$(printf 'rm -f -- %q' "$file_list")" EXIT + list0_files > "$file_list" + + lock_close 8 + + clean_files "$file_list" +} + +release_packages() { + if [[ -n $HOOKPRERELEASE ]]; then + msg "Running HOOKPRERELEASE..." + plain '%s' "${HOOKPRERELEASE}" + bash -c "${HOOKPRERELEASE}" + fi + + lock 8 "${WORKDIR}/staging.lock" \ + 'Waiting for an exclusive lock on the staging directory' + + sign_packages || return 1 + + # Make the permissions of the packages 644 otherwise the user will get access + # denied error when they try to download (rsync --no-perms doesn't seem to + # work). + find "${WORKDIR}/staging" -type f -exec chmod 644 {} + + find "${WORKDIR}/staging" -type d -exec chmod 755 {} + + + local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)" + trap "$(printf 'rm -f -- %q' "$file_list")" EXIT + list0_files > "$file_list" + + lock_close 8 + + msg "%s to upload" "$(cd "${WORKDIR}/staging" && du -hc --files0-from="$file_list" | sed -n '$s/\t.*//p')" + msg "Uploading packages..." + xargs -0r -a "$file_list" dirname -z | ssh "${REPODEST%%:*}" "$(printf 'mkdir -p -- %q && cd %q && xargs -0r mkdir -pv --' "${REPODEST#*:}"{,})" + if ! rsync ${dryrun} "${rsync_flags[@]}" \ + -0 --files-from="$file_list" \ + "${WORKDIR}/staging" \ + "${REPODEST}/" + then + error "Sync failed, try again" + return 1 + fi + + clean_files "$file_list" + + if $upload_only; then + return 0 + fi + + msg "Running db-update on repos" + ssh "${REPODEST%%:*}" "$(printf 'STAGING=%q dbscripts/db-update' "${REPODEST#*:}")" + + if [[ -n $HOOKPOSTRELEASE ]]; then + msg "Running HOOKPOSTRELEASE..." + plain '%s' "${HOOKPOSTRELEASE}" + bash -c "${HOOKPOSTRELEASE}" + fi + + return 0 +} + +main "$@" diff --git a/src/abslibre-tools/librestage b/src/abslibre-tools/librestage new file mode 100755 index 0000000..39523ac --- /dev/null +++ b/src/abslibre-tools/librestage @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# LibreStage +# Prepares packages for upload + +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages +. "$(librelib conf.sh)" + +usage() { + print "Usage: %s [REPO]" "${0##*/}" + print "Stages the package(s) build by ./PKGBUILD for upload." + echo + prose "The package(s) are staged for the named repository, or the name + of the parent directory if a repository is not named." +} + +main() { + if [[ -w / ]]; then + error "This program should be run as a regular user" + return 1 + fi + + # Parse options, set up + while getopts 'h' arg; do + case $arg in + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + local repo= + case $# in + 0) repo="$(basename "$(dirname "$PWD")")";; + 1) repo=$1;; + *) usage >&2; return 1;; + esac + + if ! [[ -e ./PKGBUILD ]]; then + error "PKGBUILD not found" + return 1 + fi + + if ! xbs -b abslibre status; then + error "There are uncommitted changes in the current directory" + return 1 + fi + + # Load configuration + load_files libretools + # ABSLIBREDEST is used by xbs release-client + check_vars libretools WORKDIR ARCHES ABSLIBREDEST || return 1 + load_files makepkg # for PKGDEST and SRCDEST, which are optional + load_files librefetch # for MIRRORS, which is optional + + # Load the PKGBUILD + load_PKGBUILD + + # Now for the main routine. + local staged=false + slock 8 "${WORKDIR}/staging.lock" \ + 'Waiting for a shared lock on the staging directory' + + # Look for makepkg output + local CARCH _pkgname pkgfile + for CARCH in "${ARCHES[@]}" any; do + for _pkgname in "${pkgname[@]}" "${pkgname[@]/%/-debug}"; do + if ! pkgfile=$(find_cached_package "$_pkgname" "$(get_full_version "$_pkgname")" "$CARCH"); then + continue + fi + + msg 'Found package: %s' "${pkgfile##*/}" + + # This little check is from devtools:commitpkg + if grep -q "packager = Unknown Packager" <(bsdtar -xOqf "$pkgfile" .PKGINFO); then + die "PACKAGER wes not set when building package" + fi + + xbs -b abslibre release-client "$repo" "$CARCH" + mkdir -p "${WORKDIR}/staging/${repo}" + if cp "$pkgfile" "${WORKDIR}/staging/${repo}/${pkgfile##*/}"; then + msg2 "%s staged on [%s]" "$_pkgname" "$repo" + staged=true + else + error "Can't put %s on [%s]" "$_pkgname" "$repo" + return 1 + fi + done + done + + # Look for librefetch output + local netfile mirror path + local srcurl srcname srcpath + for netfile in "${source[@]}"; do + for mirror in "${MIRRORS[@]}"; do + srcurl=${netfile#*::} + if [[ "$srcurl" == "$mirror"* ]]; then + if [[ $netfile = *::* ]]; then + srcname=${netfile%%::*} + else + srcname=${netfile##*/} + fi + + srcpath='' + for path in "./$srcname" "${SRCDEST:-.}/$srcname"; do + if [[ -f "$path" ]]; then + srcpath="$path" + break + fi + done + if [[ -n "$srcpath" ]]; then + msg "Found generated source file: %s" "$srcname" + local dest="${WORKDIR}/staging/other/${srcurl##"$mirror"}" + mkdir -p -- "${dest%/*}" + if cp "$srcpath" "$dest"; then + msg2 "%s staged on [%s]" "$srcname" other + staged=true + else + error "Can't put %s on [%s]" "$srcname" other + return 1 + fi + fi + break + fi + done + done + + if $staged ; then + return 0 + else + error "Nothing was staged" + return 1 + fi +} + +main "$@" @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages + +usage() { + print "Usage: %s [-h] PKGNAME [PKGNAME2 PKGNAME3...]" "${0##*/}" + print "Downloads packages from the AUR, and does basic freedom checks." + echo + prose "This script will download packages from AUR to the current + directory and check their license for nonfree issues. This does + not mean that they are free; they may be incorrectly labeled, or + have other freedom issues. It's a tool to help Parabola + packagers, not to help users install things directly from AUR." +} + +main() { + while getopts 'h' arg; do + case $arg in + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + if [[ $# -lt 1 ]]; then + usage >&2 + return 1 + fi + + . "$(librelib conf.sh)" + load_files libretools + check_vars libretools DIFFPROG || exit 1 + + local startdir="$(pwd)" + local missing_deps=() + local ret=0 + local pkg + local copy_new + local copy_old + for pkg in "$@"; do + pkg="${pkg%%[<>=]*}" # remove the version + msg "Processing package: %s" "$pkg" + copy_new="$startdir/$pkg" + copy_old= + + if [[ -f "${copy_new}/PKGBUILD" ]]; then + warning "%s already exists, will compare with new version." "$pkg" + + # Store our copy of the PKGBUILD dir + copy_old=$copy_new + copy_new="$(mktemp --tmpdir -d aur-${pkg}.new.XXXX)/$pkg" + cd "${copy_new%/*}" + fi + + msg2 "Downloading" + local url="https://aur.archlinux.org/packages/${pkg:0:2}/$pkg/$pkg.tar.gz" + set -o pipefail + if ! wget -O- -q "$url" | bsdtar xf -; then + ret=$(($ret|2)) + error "Couldn't get %s" "$pkg" + continue + fi + set +o pipefail + + if [[ -n $copy_old ]]; then + msg2 "Diffing files" + cd "$copy_new" + + # Diff all files with our difftool + local diffed=false + for file in *; do + if ! cmp -s "${copy_old}/${file}" "${copy_new}/${file}" ; then + warning "%s != %s" "${copy_old}/${file}" "${copy_new}/${file}" + diffed=true + "${DIFFPROG}" "${copy_old}/${file}" "${copy_new}/${file}" + fi + done + if $diffed; then + read -p "Press enter to continue." + fi + + # Go back to our copy to continue working + cd "$copy_old" + rm -rf -- "${copy_new%/*}" + else + cd "$copy_new" + fi + + load_PKGBUILD + + ################################################################ + + pkgbuild-check-nonfree -c + pkgbuild-summarize-nonfree $? + + ################################################################ + + local _deps=( + # depends + "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" + # mksource depends + "${mkdepends[@]}" + ) + local _dep + msg2 "Checking dependencies" + for _dep in "${_deps[@]}"; do + _dep=${_dep/[<>=]*/} + if ! is_built $_dep; then + if ! pacman -Sddp "$_dep" &>/dev/null ; then + plain "%s: will be downloaded from AUR" "$_dep" + missing_deps+=($_dep) + fi + else + plain "%s: is on repos" "$_dep" + fi + done + cd "$startdir" + done + + if [[ ${#missing_deps[*]} -gt 0 ]]; then + msg "Retrieving missing deps: %s" "${missing_deps[*]}" + "$0" "${missing_deps[@]}" + ret=$(($ret|$?)) + fi + return $ret; +} + +main "$@" diff --git a/src/chroot-tools/.gitignore b/src/chroot-tools/.gitignore new file mode 100644 index 0000000..f0969c7 --- /dev/null +++ b/src/chroot-tools/.gitignore @@ -0,0 +1,7 @@ +makechrootpkg.sh* +arch-nspawn* +mkarchroot* +!*.patch + +chcleanup +chcleanup.lib diff --git a/src/chroot-tools/HACKING.md b/src/chroot-tools/HACKING.md new file mode 100644 index 0000000..e50aa11 --- /dev/null +++ b/src/chroot-tools/HACKING.md @@ -0,0 +1,3 @@ +Unfortunately, `makechrootpkg.sh` is GPLv2 ONLY. This means that +everything that uses it must be held to GPLv2+ instead of GPLv3+. I'm +calling this anything that gets loaded into the same process as it. diff --git a/src/chroot-tools/Makefile b/src/chroot-tools/Makefile new file mode 100644 index 0000000..48a1631 --- /dev/null +++ b/src/chroot-tools/Makefile @@ -0,0 +1,27 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkglibexecdir = $(libexecdir)/libretools/chroot + +libretools-bins = chcleanup librechroot libremakepkg +libretools-libexecs += arch-nspawn chcleanup distcc-tool indent mkarchroot +libretools-libs += makechrootpkg.sh +devtools-files = makechrootpkg.sh.in mkarchroot.in arch-nspawn.in +am_clean_files += chcleanup.lib + +$(srcdir)/makechrootpkg.sh.in: $(srcdir)/%.sh.in: $(devtoolsdir)/%.in + cp $< $@ + +$(outdir)/chcleanup: $(srcdir)/chcleanup.in $(outdir)/chcleanup.lib + m4 -I$(@D) -P $< | $(edit) | install -m755 /dev/stdin $@ +$(outdir)/chcleanup.lib: $(call _am_path,$(topoutdir)/src/lib/common.sh) $(outdir)/Makefile + bash -c '. $<; declare -f _l plain msg msg2 error' > $@ + +$(outdir)/distcc-tool.pot: LIBREXGETTEXT_FLAGS+=--simple=errusage + +$(DESTDIR)$(bindir)/chcleanup: $(var)bindir $(var)libexecdir + mkdir -p $(@D) + ln -srfT $(DESTDIR)$(libexecdir)/libretools/chroot/chcleanup $@ + +am_depdirs = ../lib + +include $(topsrcdir)/automake.tail.mk diff --git a/src/chroot-tools/arch-nspawn.patch b/src/chroot-tools/arch-nspawn.patch new file mode 100644 index 0000000..dc4cb25 --- /dev/null +++ b/src/chroot-tools/arch-nspawn.patch @@ -0,0 +1,71 @@ +--- arch-nspawn.in 2016-05-10 13:48:14.303797115 -0400 ++++ arch-nspawn.ugly 2016-05-10 13:48:23.296957958 -0400 +@@ -1,4 +1,6 @@ + #!/bin/bash ++# License: GNU GPLv2 ++# + # This program 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; version 2 of the License. +@@ -14,6 +16,8 @@ + + working_dir='' + ++files=() ++ + usage() { + echo "Usage: ${0##*/} [options] working-dir [systemd-nspawn arguments]" + echo "A wrapper around systemd-nspawn. Provides support for pacman." +@@ -22,17 +26,21 @@ + echo ' -C <file> Location of a pacman config file' + echo ' -M <file> Location of a makepkg config file' + echo ' -c <dir> Set pacman cache' ++ echo ' -f <file> Copy file from the host to the chroot' ++ echo ' -s Do not run setarch' + echo ' -h This message' + exit 1 + } + + orig_argv=("$@") + +-while getopts 'hC:M:c:' arg; do ++while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + C) pac_conf="$OPTARG" ;; + M) makepkg_conf="$OPTARG" ;; + c) cache_dir="$OPTARG" ;; ++ f) files+=("$OPTARG") ;; ++ s) nosetarch=1 ;; + h|?) usage ;; + *) error "invalid argument '%s'" "$arg"; usage ;; + esac +@@ -78,6 +86,12 @@ + [[ -n $pac_conf ]] && cp $pac_conf "$working_dir/etc/pacman.conf" + [[ -n $makepkg_conf ]] && cp $makepkg_conf "$working_dir/etc/makepkg.conf" + ++ local file ++ for file in "${files[@]}"; do ++ mkdir -p "$(dirname "$working_dir$file")" ++ cp -T "$file" "$working_dir$file" ++ done ++ + sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n ${cache_dirs[@]})|g" -i "$working_dir/etc/pacman.conf" + } + # }}} +@@ -92,6 +106,7 @@ + fi + + build_mount_args ++cache_dirs+=('/repo/') + copy_hostconf + + eval $(grep '^CARCH=' "$working_dir/etc/makepkg.conf") +@@ -99,6 +114,8 @@ + armv7h) CARCH=armv7l;; + esac + ++[[ -z $nosetarch ]] || unset CARCH ++ + exec ${CARCH:+setarch "$CARCH"} systemd-nspawn -q \ + -D "$working_dir" \ + --register=no \ diff --git a/src/chroot-tools/chcleanup.in b/src/chroot-tools/chcleanup.in new file mode 100644 index 0000000..04cf8ad --- /dev/null +++ b/src/chroot-tools/chcleanup.in @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2013, 2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# If you don't see m4_include(...) below, but see function definitions +# for msg() et al., then this is a generated file, and contains some +# code from librelib. The the source distribution for full copyright +# information. +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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/>. + +# Performs chroot cleanup smartly, it only removes the unneeded packages or +# leaves you with a cleansystem +# +# See: HOOKPREBUILD + +set -eE + +DRYRUN=${DRYRUN:-false} + +# Statically include various library routines to avoid having +# dependencies on outside files. +[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools' +[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale' + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi +# Begin chcleanup.lib ########################################################## +m4_include(chcleanup.lib) +# End chcleanup.lib ############################################################ + +if [[ ! -f /.arch-chroot ]] && ! ${DRYRUN}; then + error "(chcleanup): Must be run inside of a chroot" + exit 1 +fi + +# Note: the in-chroot pkgconfdir is non-configurable, this is +# intentionally hard-coded. +source /etc/libretools.d/chroot.conf +# If we're running makepkg +if [[ -f PKGBUILD ]]; then + export CARCH="$(. /etc/makepkg.conf; printf '%s' "$CARCH")" + source ./PKGBUILD + CHROOTEXTRAPKG+=("${depends[@]}" + "${makedepends[@]}" + "${checkdepends[@]}") +fi + +msg "Cleaning chroot..." + +# Sync the local repo with pacman +cp /repo/repo.db /var/lib/pacman/sync/repo.db + +# Setup the temporary directory +TEMPDIR="$(mktemp --tmpdir -d ${0##*/}.XXXXXXXXXX)" +trap "rm -rf -- $(printf '%q' "$TEMPDIR")" EXIT + +cp -a /var/lib/pacman/sync "${TEMPDIR}/" +pkglist="${TEMPDIR}"/pkglist.txt + +# Get the full list of packages needed by dependencies, including the base system +msg2 "Creating a full list of packages..." +pacman -b "${TEMPDIR}" \ + -Sp --print-format "%n" base-devel "${CHROOTEXTRAPKG[@]}" >"$pkglist" || { + ret=$? + error "Could not create a full list of packages, exiting." + plain "This is likely caused by a dependency that could not be found." + exit $ret +} + +# Diff installed packages against a clean chroot then remove leftovers +packages=($(comm -23 <(pacman -Qq | sort -u) \ + <(sort -u "${pkglist}"))) + +if [[ ${#packages[@]} = 0 ]]; then + msg2 "No packages to remove" +else + msg2 "Removing %d packages" ${#packages[@]} + + if ${DRYRUN}; then + echo "${packages[*]}" + else + # Only remove leftovers, -Rcs removes too much + pacman --noconfirm -Rn "${packages[@]}" + fi +fi diff --git a/src/chroot-tools/chroot.conf b/src/chroot-tools/chroot.conf new file mode 100644 index 0000000..3b3c445 --- /dev/null +++ b/src/chroot-tools/chroot.conf @@ -0,0 +1,13 @@ +# The full path to the chroot is +# $CHROOTDIR/$CHROOT/$COPY +# where $COPY is set at runtime. +# See `librechroot help` for details. +CHROOTDIR=/var/lib/archbuild +CHROOT=default + +# Extra packages to have installed on the chroot. +# This is in addition to CHROOTPKG=(base-devel) +CHROOTEXTRAPKG=() +#CHROOTEXTRAPKG+=(distcc-nozeroconf socat) # for BUILDENV+=(distcc) +#CHROOTEXTRAPKG+=(ccache) # for BUILDENV+=(ccache) +#CHROOTEXTRAPKG+=(libretools) # for running libremakepkg inside the chroot diff --git a/src/chroot-tools/distcc-tool b/src/chroot-tools/distcc-tool new file mode 100755 index 0000000..d181109 --- /dev/null +++ b/src/chroot-tools/distcc-tool @@ -0,0 +1,247 @@ +#!/usr/bin/env bash +# -*- tab-width: 4; sh-basic-offset: 4 -*- +# distcc-tool + +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +# This program has very few dependencies: +# - bash: I don't know what version, I use fairly modern features +# - socat +# - cat: any version +# - rm: any version +# - sed: any version +# On Parabola, this means the packages: +# bash, coreutils, sed, socat + +if ! type gettext &>/dev/null; then + gettext() { echo "$@"; } +fi + +q0="$(printf '%q' "$0")" # quoted $0 + +panic() { + echo "$(gettext 'panic: malformed call to internal function')" >&2 + exit 1 +} + +error() { + mesg="$(gettext "$1")"; shift + printf "$(gettext 'ERROR:') $mesg\n" "$@" >&2 + exit 1 +} + +print() { + local mesg=$1 + shift + printf -- "$(gettext "$mesg")\n" "$@" +} + +usage() { + print "Usage: %s COMMAND [COMMAND-ARGS]" "$q0" + print "Tool for using distcc within a networkless chroot" + echo + print "Commands:" + print ' help print this message' + print ' odaemon CHROOTPATH daemon to run outside of the chroot' + print ' idaemon DISTCC_HOSTS daemon to run inside of the chroot' + print ' rewrite DISTCC_HOSTS prints a rewritten version of DISTCC_HOSTS' + print ' client HOST PORT connects stdio to TCP:$HOST:$PORT' + print 'Commands: for internal use' + print ' server counterpart to client; spawned by odaemon' +} + +errusage() { + if [[ $# -gt 0 ]]; then + fmt="$(gettext "$1")"; shift + printf "$(gettext 'ERROR:') $fmt\n" "$@" >&2 + fi + usage >&2 + exit 1 +} + +main() { + local cmd=$1 + shift + case "$cmd" in + help) + [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd" + usage;; + odaemon|idaemon|rewrite) + [[ $# -eq 1 ]] || errusage '%s: invalid number of arguments' "$cmd" + $cmd "$@";; + client) + [[ $# -eq 2 ]] || errusage '%s: invalid number of arguments' "$cmd" + $cmd "$@";; + server) + [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd" + $cmd "$@";; + *) errusage 'unknown subcommand: %s' "$cmd";; + esac +} + +################################################################################ +# DISTCC_HOSTS parser # +################################################################################ + +# usage: parse_DISTCC_HOSTS true|false DISTCC_HOSTS +# parses DISTCC_HOSTS and: +# $1==true : It sets up port forwarding for inside the choot, sleep forever +# $1==false: Prints a modified version of DISTCC_HOSTS that uses the forwarded +# ports that were set up when $1==true. +parse_DISTCC_HOSTS() { + { [[ $# -eq 2 ]] && { [[ $1 == true ]] || [[ $1 == false ]]; }; } || panic + local forward_ports=$1 + local DISTCC_HOSTS=$2 + + local pids=() # child pids + local newhosts=() + local newport=8000 # next port to be used for port forwarding + + # This is based on the grammar specified in distcc(1) + local HOSTSPEC + for HOSTSPEC in $(sed 's/#.*//' <<<"$DISTCC_HOSTS"); do + case "$HOSTSPEC" in + # LOCAL_HOST + localhost|localhost/*|--localslots=*|--localslots_cpp=*) + # "localhost" runs commands directly, not talking to distccd at + # localhost, use an IP or real hostname for that. + # So, just pass these through. + newhosts+=("$HOSTSPEC") + ;; + # SSH_HOST + *@*) + # SSH_HOST doesn't allow custom port numbers, and even if it + # did, ssh would complain about MITM. Instead, we'll count on + # ssh ProxyCommand being configured to use `client`. + newhosts+=("$HOSTSPEC") + ;; + # GLOBAL_OPTION + --*) + # pass these through + newhosts+=("$HOSTSPEC") + ;; + # ZEROCONF + +zeroconf) + error "%s does not support the +zeroconf option" "$q0" + exit 1 + ;; + # TCP_HOST or OLDSTYLE_TCP_HOST + *) + declare HOSTID= PORT= LIMIT= OPTIONS= + if [[ $HOSTSPEC =~ ^([^:/]+)(:([0-9]+))?(/([0-9]+))?(,.*)?$ ]]; then + # TCP_HOST + HOSTID=${BASH_REMATCH[1]} + PORT=${BASH_REMATCH[3]} + LIMIT=${BASH_REMATCH[5]} + OPTIONS=${BASH_REMATCH[6]} + elif [[ $HOSTSPEC =~ ^([^:/]+)(/([0-9]+))?(:([0-9]+))?(,.*)?$ ]]; then + # OLDSTYLE_TCP_HOST + HOSTID=${BASH_REMATCH[1]} + LIMIT=${BASH_REMATCH[3]} + PORT=${BASH_REMATCH[5]} + OPTIONS=${BASH_REMATCH[6]} + else + error "Could not parse HOSTSPEC: %s" "$HOSTSPEC" + fi + + # set up port forwaring + if $forward_ports; then + socat TCP-LISTEN:${newport},fork SYSTEM:"$q0 client $HOSTID ${PORT:-3632}" & + pids+=($!) + fi + + # add the forwarded port + local newhost="127.0.0.1:$newport" + [[ -z $LIMIT ]] || newhost+="/$LIMIT" + [[ -z $OPTIONS ]] || newhost+="$OPTIONS" + newhosts+=("$newhost") + : $((newport++)) + ;; + esac + done + if $forward_ports; then + if [[ -z "${pids[*]}" ]]; then + # listen on port 8000, but immediatly close, just so that we are + # listening on something + socat TCP-LISTEN:${newport},fork SYSTEM:true & + pids+=($!) + fi + trap "kill -- ${pids[*]}" EXIT + wait "${pids[@]}" + else + printf '%s\n' "${newhosts[*]}" + fi +} + +################################################################################ +# Port forwarding primitives # +################################################################################ + +# Usage: server +# Reads "host port" from the first line of stdin, then connects stdio to the +# specified TCP socket. +server() { + [[ $# -eq 0 ]] || panic + local host port + read -r host port + socat STDIO TCP:"$host:$port" +} + +# Usage: client HOST PORT +# For usage inside of a chroot. It talks through the UNIX-domain socket to an +# instance of `server` outside, in order to connect stdio to the specified TCP +# socket. +client() { + [[ $# -eq 2 ]] || panic + local file=/socket + { printf '%s\n' "$*"; cat; } | socat UNIX-CONNECT:"$file" STDIO +} + +################################################################################ +# High-level routines # +################################################################################ + +# Usage: odaemon CHROOTPATH +# Listens on "$CHROOTPATH/socket" and spawns a `server` for each new connection. +odaemon() { + [[ $# -eq 1 ]] || panic + local chrootpath=$1 + + umask 111 + socat UNIX-LISTEN:"$chrootpath/socket",fork SYSTEM:"$q0 server" & + trap "kill -- $!; rm -f -- $(printf '%q' "$chrootpath/socket")" EXIT + wait +} + +# Usage: idaemon DISTCC_HOSTS +# Sets things up inside of the chroot to forward distcc hosts out. +idaemon() { + [[ $# -eq 1 ]] || panic + parse_DISTCC_HOSTS true "$1" +} + +# Usage: rewrite DISTCC_HOSTS +# Prints a modified version of DISTCC_HOSTS for inside the chroot. +rewrite() { + [[ $# -eq 1 ]] || panic + parse_DISTCC_HOSTS false "$1" +} + +main "$@" diff --git a/src/chroot-tools/hooks-chcleanup.sh b/src/chroot-tools/hooks-chcleanup.sh new file mode 100644 index 0000000..7afb13d --- /dev/null +++ b/src/chroot-tools/hooks-chcleanup.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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 Affero 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 -euE + +hook_pre_build+=("clean_chroot") + +clean_chroot() ( + set +x + local copydir=$1 + if $INCHROOT; then + cd /startdir + "$(librelib chroot/chcleanup)" + else + librechroot "${librechroot_flags[@]}" clean-pkgs + fi +) diff --git a/src/chroot-tools/hooks-check.sh b/src/chroot-tools/hooks-check.sh new file mode 100644 index 0000000..850516b --- /dev/null +++ b/src/chroot-tools/hooks-check.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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 Affero 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 -euE + +hook_check_pkgbuild+=("check_pkgbuild_nonfree") +check_pkgbuild_nonfree() { + local s=0 + sudo -EH -u "$LIBREUSER" pkgbuild-check-nonfree -f || s=$? + pkgbuild-summarize-nonfree $s +} + +#hook_check_pkgbuild+=("check_pkgbuild_namcap") +check_pkgbuild_namcap() { + sudo -EH -u "$LIBREUSER" namcap PKGBUILD +} + +#hook_check_pkg+=("check_pkg") +check_pkg() { + # TODO + : +} diff --git a/src/chroot-tools/hooks-distcc.sh b/src/chroot-tools/hooks-distcc.sh new file mode 100644 index 0000000..bb234b8 --- /dev/null +++ b/src/chroot-tools/hooks-distcc.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This file is part of Parabola. +# +# Parabola 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 2 of the License, or +# (at your option) any later version. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +set -euE + +hook_pre_build+=("distcc_start") +hook_post_build+=("distcc_stop") + +_distcc_check() { + local copydir=$1 + local home=$2 + + local files=( + "$copydir/bin/distcc-tool" + "$copydir/run/distcc-tool.pid" + "$home/.makepkg.conf" + "$home/.ssh/config" + ) + + local file_err=false + for files in "${files[@]}"; do + if [[ -f $file ]]; then + file_err=true + error "Auto-generated file already exists, remove it: %s" "$file" + fi + done + if $file_err; then + exit 1 + fi +} + +distcc_start() { + local copydir=$1 + + # Because /{,usr/}{,s}bin are all symlinked together for + # fileystem 2013.05-2 and up, I can take shortcuts when checking for + # existance of programs. + if $NONET && [[ -f "$copydir/bin/socat" && -f "$copydir/bin/distcc" ]]; then + local home + if $INCHROOT; then + home=$LIBREHOME + else + home="$copydir/build" + fi + + _distcc_check + + local _distcc_tool="$(librelib chroot/distcc-tool)" + install -m755 "$_distcc_tool" "$copydir/bin/distcc-tool" + + mkdir -p "$home/.ssh" + + printf '%s\n' \ + '/bin/distcc-tool idaemon "$DISTCC_HOSTS" &' \ + 'DISTCC_HOSTS="$(/bin/distcc-tool rewrite "$DISTCC_HOSTS")"' \ + > "$home/.makepkg.conf" + + printf '%s\n' \ + 'Host *' \ + ' ProxyCommand /bin/distcc-tool client %h %p' \ + > "$home/.ssh/config" + + "$_distcc_tool" odaemon "$copydir" & + echo $! > "$copydir/run/distcc-tool.pid" + fi +} + +distcc_stop() { + local copydir=$1 + + local home + if $INCHROOT; then + home=$LIBREHOME + else + home="$copydir/build" + fi + + if [[ -f "$copydir/run/distcc-tool.pid" ]]; then + + odaemon=$(< "$copydir/distcc-tool.pid") + kill -- "$odaemon" + + rm -f -- \ + "$home/.makepkg.conf" \ + "$home/.ssh/config" \ + "$copydir/bin/distcc-tool" \ + "$copydir/run/distcc-tool.pid" + fi +} diff --git a/src/chroot-tools/indent b/src/chroot-tools/indent new file mode 100755 index 0000000..0f047c6 --- /dev/null +++ b/src/chroot-tools/indent @@ -0,0 +1,57 @@ +#!/usr/bin/env perl +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +use warnings; +use strict; + +use constant BUFFER_SIZE => 40; +binmode(STDIN); +binmode(STDOUT); + +exit(1) if ($#ARGV != 0); +my $indent = $ARGV[0]; + +# 0: no indent has been printed for this line, an indent WILL need to be printed +# 1: an indent needs to be printed for this line IFF there is any more output on it +# 2: no indent (currently) needs to be printed for this line +my $print_indent = 0; + +my $buffer; +my $size; +my $c; +while (1) { + $size = sysread(STDIN, $buffer, BUFFER_SIZE); + last if ($size < 1); + for (0..$size-1) { + $c = substr($buffer, $_, 1); + if ($c eq "\n") { + syswrite(STDOUT, $indent) if ($print_indent == 0); + syswrite(STDOUT, $c, 1); + $print_indent = 0; + } elsif ($c eq "\r") { + syswrite(STDOUT, $c, 1); + $print_indent = 1 if ($print_indent == 2); + } else { + syswrite(STDOUT, $indent) if ($print_indent < 2); + syswrite(STDOUT, $c, 1); + $print_indent = 2; + } + } +} diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot new file mode 100755 index 0000000..cf564ed --- /dev/null +++ b/src/chroot-tools/librechroot @@ -0,0 +1,509 @@ +#!/usr/bin/env bash +set -euE +# librechroot + +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This file is part of Parabola. +# +# Parabola 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 2 of the License, or +# (at your option) any later version. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +# HACKING: if a command is added or removed, it must be changed in 4 places: +# - the usage() text +# - the commands=() array +# - the case statement in main() that checks the number of arguments +# - the case statement in main() that runs them + +. "$(librelib conf)" +load_files chroot + +. libremessages + +shopt -s nullglob +umask 0022 + +################################################################################ +# Wrappers for files in ${pkglibexecdir}/chroot/ # +################################################################################ + +readonly _arch_nspawn="$(librelib chroot/arch-nspawn)" +readonly _mkarchroot="$(librelib chroot/mkarchroot)" +readonly _makechrootpkg="$(librelib chroot/makechrootpkg.sh)" + +arch_nspawn_flags=() +sysd_nspawn_flags=() + +hack_arch_nspawn_flags() { + local copydir="$1" + + local makepkg_conf="$copydir/etc/makepkg.conf" + + OPTIND=1 + set -- ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} + while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + M) makepkg_conf="$OPTARG" ;; + *) :;; + esac + done + + # Detect the architecture of the chroot + local CARCH + if [[ -f "$makepkg_conf" ]]; then + eval $(grep '^CARCH=' "$makepkg_conf") + else + CARCH="$(uname -m)" + fi + + if [[ "$CARCH" == armv7h ]] && ! setarch armv7l 2>/dev/null; then + # We're running an ARM chroot on a non-ARM processor + + # Make sure that qemu-static is set up with binfmt_misc + if [[ $(grep -xF \ + -e 'enabled'\ + -e 'interpreter /usr/bin/qemu-arm-static' \ + /proc/sys/fs/binfmt_misc/arm 2>/dev/null |wc -l) -lt 2 ]]; then + error 'Cannot cross-compile for ARM on x86' + plain 'This requires a binfmt_misc entry for qemu-arm-static,' + plain 'which is provided by the %s package.' binfmt-qemu-static + plain 'If you have this, you may need to restart %s.' systemd-binfmt.service + return 1 + fi + + # Let qemu/binfmt_misc do its thing + arch_nspawn_flags+=(-f /usr/bin/qemu-arm-static -s) + + # The -any packages are built separately for ARM from + # x86, so if we use the same CacheDir as the x86 host, + # then there will be PGP errors. + mkdir -p /var/cache/pacman/pkg-arm + arch_nspawn_flags+=(-c /var/cache/pacman/pkg-arm) + fi +} + +# Usage: arch-nspawn $copydir $cmd... +arch-nspawn() { + local copydir=$1; shift + local cmd=("$@") + + local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"}) + hack_arch_nspawn_flags "$copydir" + + "$_arch_nspawn" \ + ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \ + "$copydir" \ + ${sysd_nspawn_flags+"${sysd_nspawn_flags[@]}"} \ + -- \ + "${cmd[@]}" +} + +# Usage: mkarchroot $copydir $pkgs... +mkarchroot() { + local copydir=$1; shift + local pkgs=("$@") + + local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"}) + hack_arch_nspawn_flags "$copydir" + + unshare -m "$_mkarchroot" \ + ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \ + "$copydir" \ + "${pkgs[@]}" +} + +# Usage: _makechrootpkg $function $arguments... +# Don't load $_makechrootpkg directly because it doesn't work with -euE +_makechrootpkg() ( + set +euE + . "$_makechrootpkg" + "$@" +) + +################################################################################ +# Utility functions # +################################################################################ + +# Usage: make_empty_repo $copydir +make_empty_repo() { + local copydir=$1 + mkdir -p "${copydir}/repo" + bsdtar -czf "${copydir}/repo/repo.db.tar.gz" -T /dev/null + ln -s "repo.db.tar.gz" "${copydir}/repo/repo.db" +} + +# Usage: chroot_add_to_local_repo $copydir $pkgfiles... +chroot_add_to_local_repo() { + local copydir=$1; shift + mkdir -p "$copydir/repo" + local pkgfile + for pkgfile in "$@"; do + cp "$pkgfile" "$copydir/repo" + pushd "$copydir/repo" >/dev/null + repo-add repo.db.tar.gz "${pkgfile##*/}" + popd >/dev/null + done +} + +# Print code to set $rootdir and $copydir; blank them on error +calculate_directories() { + # Don't assume that CHROOTDIR or CHROOT are set, + # but assume that COPY is set. + local rootdir copydir + + if [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then + rootdir="${CHROOTDIR}/${CHROOT}/root" + else + rootdir='' + fi + + if [[ ${COPY:0:1} = / ]]; then + copydir=$COPY + elif [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then + copydir="${CHROOTDIR}/${CHROOT}/${COPY}" + else + copydir='' + fi + + declare -p rootdir + declare -p copydir +} + +check_mountpoint() { + local file=$1 + local mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')" + local mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }")) + ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}" +} + +################################################################################ +# Main program # +################################################################################ + +usage() { + eval "$(calculate_directories)" + print "Usage: %s [OPTIONS] COMMAND [ARGS...]" "${0##*/}" + print 'Interacts with an archroot (arch chroot).' + echo + prose 'This is configured with `chroot.conf`, either in + `/etc/libretools.d/`, or `$XDG_CONFIG_HOME/libretools/`. + The variables you may set are $CHROOTDIR, $CHROOT, and + $CHROOTEXTRAPKG.' + echo + prose 'There may be multiple chroots; they are stored in $CHROOTDIR.' + echo + prose 'Each chroot is named; the default is configured with $CHROOT.' + echo + prose 'Each named chroot has a master clean copy (named `root`), and any + number of other named copies; the copy used by default is the + current username (or $SUDO_USER, or `copy` if root).' + echo + prose 'The full path to the chroot copy is "$CHROOTDIR/$CHROOT/$COPY", + unless the copy name is manually specified as an absolute path, + in which case, that path is used.' + echo + prose 'The current settings for the above variables are:' + printf ' CHROOTDIR : %s\n' "${CHROOTDIR:-$(_ 'ERROR: NO SETTING')}" + printf ' CHROOT : %s\n' "${CHROOT:-$(_ 'ERROR: NO SETTING')}" + printf ' COPY : %s\n' "$COPY" + printf ' rootdir : %s\n' "${rootdir:-$(_ 'ERROR')}" + printf ' copydir : %s\n' "${copydir:-$(_ 'ERROR')}" + echo + prose 'If the chroot or copy does not exist, it will be created + automatically. A chroot by default contains the packages in the + group "base-devel" and any packages named in $CHROOTEXTRAPKG. + Unless the `-C` or `-M` flags are used, the configuration files + that this program installs are the stock versions supplied in the + packages, not the versions from your host system. Other tools + (such as libremakepkg) may alter the configuration.' + echo + prose 'This command will make the following configuration changes in the + chroot:' + bullet 'overwrite `/etc/libretools.d/chroot.conf`' + bullet 'overwrite `/etc/pacman.d/mirrorlist`' + bullet 'set `CacheDir` in `/etc/pacman.conf`' + prose 'If a new `pacman.conf` is inserted with the `-C` flag, the change + is made after the file is copied in; the `-C` flag doesn'"'"'t + stop the change from being effective.' + echo + prose 'The processor architecture of the chroot is determined + by the by `CARCH` variable in the `/etc/makepkg.conf` + file inside of the chroot.' + echo + prose 'The `-A CARCH` flag is *almost* simply an alias for' + printf ' %s\n' \ + '-C "/usr/share/pacman/defaults/pacman.conf.$CARCH" \' \ + '-M "/usr/share/pacman/defaults/makepkg.conf.$CARCH"' + prose 'However, before doing that, it actually makes a temporary copy of + `pacman.conf`, and sets the `Architecture` line to match the + `CARCH` line in `makepkg.conf`.' + echo + prose 'Creating a copy, deleting a copy, or syncing a copy can be fairly + slow; but are very fast if $CHROOTDIR is on a btrfs partition.' + echo + print 'Options:' + flag "-n <$(_ CHROOT)>" 'Name of the chroot to use' + flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the copy to use' + flag '-N' 'Disable networking in the chroot' + flag "-C <$(_ FILE)>" 'Copy this file to `$copydir/etc/pacman.conf`' + flag "-M <$(_ FILE)>" 'Copy this file to `$copydir/etc/makepkg.conf`' + flag "-A <$(_ CARCH)>" 'Set the architecture of the copy; simply an + alias for the `-C` and `-M` flags, see above.' + flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write' + flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only' + echo + print 'Commands:' + print ' Create/copy/delete:' + flag 'noop|make' 'Do not do anything, but still creates the chroot + copy if it does not exist' + flag 'sync' 'Sync the copy with the clean (`root`) copy' + flag 'delete' 'Delete the chroot copy' + print ' Dealing with packages:' + flag "install-file $(_ FILES...)" 'Like `pacman -U FILES...`' + flag "install-name $(_ NAMES...)" 'Like `pacman -S NAMES...`' + flag 'update' 'Like `pacman -Syu`' + flag 'clean-pkgs' 'Remove all packages from the chroot copy that + are not in base-devel, $CHROOTEXTRAPKG, or named + as a dependency in the file `/startdir/PKGBUILD` + in the chroot copy' + print ' Other:' + flag "run $(_ CMD...)" 'Run CMD in the chroot copy' + flag 'enter' 'Enter an interactive shell in the chroot copy' + flag 'clean-repo' 'Clean /repo in the chroot copy' + flag 'help' 'Show this message' +} +readonly commands=( + noop make sync delete + install-file install-name update clean-pkgs + run enter clean-repo help +) + +# Globals: $CHROOTDIR, $CHROOT, $COPY, $rootdir and $copydir +main() { + COPY=$LIBREUSER + [[ $COPY != root ]] || COPY=copy + + local mode=enter + while getopts 'n:l:NC:M:A:w:r:' opt; do + case $opt in + n) CHROOT=$OPTARG;; + l) COPY=$OPTARG;; + N) sysd_nspawn_flags+=(--private-network);; + C|M) arch_nspawn_flags+=(-$opt "$OPTARG");; + A) + if ! [[ -f "/usr/share/pacman/defaults/pacman.conf.$OPTARG" && -f "/usr/share/pacman/defaults/makepkg.conf.$OPTARG" ]]; then + error 'Unsupported architecture: %s' "$OPTARG" + plain 'See the files in %q for valid architectures.' /usr/share/pacman/defaults/ + return 1; + fi + trap 'rm -f -- "$tmppacmanconf"' EXIT + tmppacmanconf="$(mktemp --tmpdir librechroot-pacman.conf.XXXXXXXXXX)" + < "/usr/share/pacman/defaults/pacman.conf.$OPTARG" sed -r "s|^#?\\s*Architecture.+|Architecture = ${OPTARG}|g" > "$tmppacmanconf" + arch_nspawn_flags+=( + -C "$tmppacmanconf" + -M "/usr/share/pacman/defaults/makepkg.conf.$OPTARG" + );; + w) sysd_nspawn_flags+=("--bind=$OPTARG");; + r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + error "Must specify a command" + usage >&2 + return 1 + fi + mode=$1 + if ! in_array "$mode" "${commands[@]}"; then + error "Unrecognized command: %s" "$mode" + usage >&2 + return 1 + fi + shift + case "$mode" in + noop|make|sync|delete|update|enter|clean-pkgs|clean-repo) + if [[ $# -gt 0 ]]; then + error 'Command `%s` does not take any arguments: %s' "$mode" "$*" + usage >&2 + return 1 + fi + :;; + install-file) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one file' "$mode" + usage >&2 + return 1 + else + local missing=() + local file + for file in "$@"; do + if ! [[ -f $file ]]; then + missing+=("$file") + fi + done + if [[ ${#missing[@]} -gt 0 ]]; then + error "%s: file(s) not found: %s" "$mode" "${missing[*]}" + return 1 + fi + fi + :;; + install-name) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one package name' "$mode" + usage >&2 + return 1 + fi + :;; + run) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one argument' "$mode" + usage >&2 + return 1 + fi + :;; + esac + + + if [[ $mode == help ]]; then + usage + return 0 + fi + + check_vars chroot CHROOTDIR CHROOT + eval "$(calculate_directories)" + + readonly LIBREUSER LIBREHOME + readonly CHROOTDIR CHROOT COPY + readonly rootdir copydir + readonly mode + + ######################################################################## + + if (( EUID )); then + error "This program must be run as root." + return 1 + fi + + umask 0022 + + # XXX: SYSTEMD-STDIN HACK + if ! [[ -t 0 ]]; then + error "Input is not a TTY" + plain "https://labs.parabola.nu/issues/431" + plain "https://bugs.freedesktop.org/show_bug.cgi?id=70290" + prose "Due to a bug in systemd-nspawn, redirecting stdin is not + supported." >&2 + return 1 + fi + + # Keep this lock for as long as we are running + # Note that '9' is the same FD number as in mkarchroot et al. + lock 9 "$copydir.lock" \ + "Waiting for existing lock on chroot copy to be released: [%s]" "$COPY" + + if [[ $mode != delete ]]; then + if ! check_mountpoint "$copydir.lock"; then + error "Chroot copy is mounted with nosuid or noexec options: [%s]" "$COPY" + return 1 + fi + + if [[ ! -d $rootdir ]]; then + msg "Creating 'root' copy for chroot [%s]" "$CHROOT" + mkarchroot "$rootdir" base-devel + make_empty_repo "$rootdir" + fi + + if [[ ! -d $copydir ]] || [[ $mode == sync ]]; then + msg "Syncing copy [%s] with root copy" "$COPY" + _makechrootpkg sync_chroot "$CHROOTDIR/$CHROOT" "$COPY" + fi + + # Note: the in-chroot pkgconfdir is non-configurable, this is + # intentionally hard-coded. + mkdir -p "$copydir/etc/libretools.d" + { + if [[ ${#CHROOTEXTRAPKG[*]} -eq 0 ]]; then + echo 'CHROOTEXTRAPKG=()' + else + printf 'CHROOTEXTRAPKG=(' + printf '%q ' "${CHROOTEXTRAPKG[@]}" + printf ')\n' + fi + } > "$copydir"/etc/libretools.d/chroot.conf + + # "touch" the chroot first + # this will + # - overwrite '/etc/pacman.d/mirrorlist'" + # - set 'CacheDir' in \`/etc/pacman.conf'" + # - apply -C or -M flags + arch-nspawn "$copydir" true + trap EXIT # clear the trap to remove the tmp pacman.conf from -A + arch_nspawn_flags=() # XXX dirty hack, don't apply -C or -M again + fi + + ######################################################################## + + case "$mode" in + # Creat/copy/delete + noop|make|sync) :;; + delete) + if [[ -d $copydir ]]; then + _makechrootpkg delete_chroot "$copydir" + fi + ;; + + # Dealing with packages + install-file) + _makechrootpkg install_packages "$copydir" "$@" + chroot_add_to_local_repo "$copydir" "$@" + ;; + install-name) + arch-nspawn "$copydir" pacman -Sy -- "$@" + ;; + update) + arch-nspawn "$copydir" pacman -Syu --noconfirm + ;; + clean-pkgs) + trap "rm -f -- $(printf '%q ' "$copydir"/{bin/chcleanup,chrootexec})" EXIT + install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup" + printf '%s\n' \ + '#!/bin/bash' \ + 'mkdir -p /startdir' \ + 'cd /startdir' \ + '/bin/chcleanup' \ + > "$copydir/chrootexec" + chmod 755 "$copydir/chrootexec" + arch-nspawn "$copydir" /chrootexec + ;; + + # Other + run) + arch-nspawn "$copydir" "$@" + ;; + enter) + arch-nspawn "$copydir" bash + ;; + clean-repo) + rm -rf "${copydir}"/repo/* + make_empty_repo "$copydir" + ;; + esac +} + +main "$@" diff --git a/src/chroot-tools/libremakepkg b/src/chroot-tools/libremakepkg new file mode 100755 index 0000000..a226e38 --- /dev/null +++ b/src/chroot-tools/libremakepkg @@ -0,0 +1,289 @@ +#!/usr/bin/env bash +set -euE +# libremakepkg + +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This file is part of Parabola. +# +# Parabola 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 2 of the License, or +# (at your option) any later version. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +. "$(librelib conf)" +. "$(librelib messages)" +. "$(librelib chroot/makechrootpkg.sh)" + +set -o pipefail +shopt -s nullglob +umask 0022 + +# Global variables: +readonly _indent="$(librelib chroot/indent)" +readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false) +NONET=true # can be changed with the -N flag +# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf +# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf +# LIBREUSER, LIBREHOME are set by conf.sh +librechroot_flags=() + +# Hooks ######################################################################## + +hook_pre_build=(:) +hook_post_build=(:) +hook_check_pkgbuild=(:) +hook_check_pkg=(:) +. "$(librelib chroot/hooks-chcleanup.sh)" +. "$(librelib chroot/hooks-check.sh)" +. "$(librelib chroot/hooks-distcc.sh)" + +# Boring/mundane functions ##################################################### + +indent() { + "$_indent" ' | ' +} + +# Usage: exit_copy $copydir $src_owner +# End immediately, but copy log files out +exit_copy() { + local copydir=$1 + local src_owner=$2 + if ! $INCHROOT; then + msg "Copying log and package files out of the chroot..." + move_products "$copydir" "$src_owner" + fi +} + +# Usage; run_hook $hookname $args... +run_hook() { + local hookname=$1; shift + local hookvar="hook_${hookname}[@]" + + local fails=() + for hook in "${!hookvar}"; do + "$hook" "$@" || fails+=("$hook") + done |& indent + + if [[ ${#fails[@]} -gt 0 ]]; then + error "Failure(s) in %s: %s" "$hookname" "${fails[*]}" + return 1 + else + return 0 + fi +} + +# Usage: add_to_local_repo $copydir $pkgfiles... +add_to_local_repo() { + local copydir=$1; shift + mkdir -p "$copydir/repo" + local pkgfile + for pkgfile in "$@"; do + cp "$pkgfile" "$copydir/repo" + pushd "$copydir/repo" >/dev/null + repo-add repo.db.tar.gz "${pkgfile##*/}" + popd >/dev/null + done +} + +hook_post_build+=('cleanup') +cleanup() { + local copydir=$1 + rm -f -- "$copydir"/chroot{prepare,build} +} + +build() ( + local copydir=$1 + local repack=$2 + local makepkg_args=("${@:3}") + + local run_ynet=() + local run_nnet=() + if $INCHROOT; then + run_ynet=(unshare) + run_nnet=(unshare -n) + else + run_ynet=(librechroot "${librechroot_flags[@]}" run) + run_nnet=(librechroot "${librechroot_flags[@]}" -N run) + fi + $NONET || run_nnet=("${run_ynet[@]}") + + prepare_chroot "$copydir" "$LIBREHOME" "$repack" false + "${run_ynet[@]}" /chrootprepare "${makepkg_args[@]}" |& indent + run_hook pre_build "$copydir" + trap "run_hook post_build $(printf '%q' "$copydir")" EXIT + "${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" |& indent +) + +# The main program ############################################################# + +usage() { + print "Usage: %s [options]" "${0##*/}" + print 'This program will build your package.' + echo + prose 'If run from outside of a chroot, command will make the following + configuration changes in the chroot:' + bullet 'whatever changes `librechroot` makes.' + bullet 'set `{PKG,SRC,SRCPKG,LOG}DEST` in `/etc/makepkg.conf`' + bullet 'set `PACKAGER` in `/etc/makepkg.conf` to reflect the value + outside of the chroot.' + bullet '(maybe) delete `/build/.makepkg.conf`' + bullet '(maybe) delete `/build/.ssh/config`' + prose 'If run from inside of a chroot, this command will:' + bullet '(maybe) delete `~/.makepkg.conf`' + bullet '(maybe) delete `~/.ssh/config`' + prose 'The above "maybe"s happen as part of the workarounds to make + distcc work in a network-less environment. They will happen if + both `socat` and `distcc` are installed in the chroot.' + echo + prose 'The `-n` and `-l` options behave identically to librechroot, see + the documentation there.' + echo + print 'Options:' + print ' %s options:' librechroot + flag "-n <$(_ CHROOT)>" 'Name of the chroot to use' + flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use' + flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write' + flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only' + print ' %s options:' libremakepkg + flag '-N' "Don't disable networking during build() and + package(). PLEASE don't use this unless you + have a special reason, its use is a violation + of Parabola policy." + flag '-R' 'Repackage contents of the package without rebuilding' + flag '-h' 'Show this message' +} + +# Convenience method for use in option parsing +err_chflag() { + local flag=$1 + error 'The -%s flag does not make sense inside of a chroot' "$flag" + return 1 +} + +main() { + # Initial variable values ############################################## + local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER") + local makepkg_args=(-s --noconfirm -L) + local repack=false + local chroot='' + + # Parse command line options ########################################### + while getopts 'n:l:w:r:NRh' flag ; do + case "${flag}" in + n) if $INCHROOT; then err_chflag "$flag"; else + chroot=$OPTARG; fi;; + l) if $INCHROOT; then err_chflag "$flag"; else + copy=$OPTARG; fi;; + w|r) if $INCHROOT; then err_chflag "$flag"; else + librechroot_flags+=(-$flag "$OPTARG"); fi;; + N) NONET=false;; + R) repack=true; makepkg_args+=(-R);; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# != 0 ]]; then + error 'Extra arguments: %s' "$*" + return 1 + fi + + # Resolve the chroot path ############################################## + local copydir + if $INCHROOT; then + copydir='/' + else + load_files chroot + check_vars chroot CHROOTDIR CHROOT + [[ -z ${chroot} ]] || CHROOT=$chroot + if [[ ${copy:0:1} = / ]]; then + copydir=$copy + else + copydir="${CHROOTDIR}/${CHROOT}/${copy}" + fi + unset CHROOTDIR CHROOTEXTRAPKG + fi + unset chroot + + # Load makepkg configuration ########################################### + # Note that all of these are globals + PKGDEST="$(get_var makepkg PKGDEST "$PWD")" + SRCDEST="$(get_var makepkg SRCDEST "$PWD")" + SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")" + LOGDEST="$(get_var makepkg LOGDEST "$PWD")" + MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')" + PACKAGER="$(get_var makepkg PACKAGER '')" + + # Quick sanity check ################################################### + + if (( EUID )); then + error "This program must be run as root" + exit 1 + fi + + if [[ ! -f PKGBUILD ]]; then + # This is the message used by makepkg + error "PKGBUILD does not exist." + exit 1 + fi + + # Make sure that the various *DEST directories exist + mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST" + + # OK, we are starting now ############################################## + + if $INCHROOT; then + lock 9 "/build/.lock" \ + "Waiting for existing lock on build directory to be released" + else + librechroot_flags+=( + -r "$PWD:/startdir_host" + -r "$SRCDEST:/srcdest_host" + -n "$CHROOT" + -l "$copy" + ) + + # Obtain a lock on the chroot + lock 9 "$copydir.lock" \ + "Waiting for existing lock on chroot copy to be released: [%s]" "$copy" + # Create the chroot if it does not exist + msg 'Initializing the chroot...' + librechroot "${librechroot_flags[@]}" make |& indent + fi + + # Set target CARCH + # note that we waited until after locking/creating the chroot to do this + export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)" + + # Pre-build + msg 'Starting pre-build activities...' + run_hook check_pkgbuild + msg 'Downloading sources...' + download_sources "$copydir" "$LIBREUSER" |& indent + + # Build + msg 'Starting to build the package...' + trap "exit_copy '$copydir' '$LIBREUSER'" EXIT + build "$copydir" "$repack" "${makepkg_args[@]}" + + # Post-build + msg 'Starting post-build activities...' + run_hook check_pkg + add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent +} + +main "$@" diff --git a/src/chroot-tools/makechrootpkg.sh.patch b/src/chroot-tools/makechrootpkg.sh.patch new file mode 100644 index 0000000..d5c6fca --- /dev/null +++ b/src/chroot-tools/makechrootpkg.sh.patch @@ -0,0 +1,342 @@ +--- makechrootpkg.sh.in 2016-02-08 17:17:11.848386338 -0500 ++++ makechrootpkg.sh.ugly 2016-04-14 22:01:05.923288181 -0400 +@@ -1,4 +1,6 @@ + #!/bin/bash ++# License: GNU GPLv2 ++# + # This program 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; version 2 of the License. +@@ -12,6 +14,7 @@ + + shopt -s nullglob + ++init_variables() { + default_makepkg_args=(-s --noconfirm -L --holdver) + makepkg_args=("${default_makepkg_args[@]}") + repack=false +@@ -29,9 +32,10 @@ + bindmounts_rw=() + + copy=$USER +-[[ -n $SUDO_USER ]] && copy=$SUDO_USER ++[[ -n ${SUDO_USER:-} ]] && copy=$SUDO_USER + [[ -z "$copy" || $copy = root ]] && copy=copy + src_owner=${SUDO_USER:-$USER} ++} + + usage() { + echo "Usage: ${0##*/} [options] -r <chrootdir> [--] [makepkg args]" +@@ -68,28 +72,51 @@ + } + + # {{{ functions ++# Usage: load_vars $makepkg_conf ++# Globals: ++# - SRCDEST ++# - SRCPKGDEST ++# - PKGDEST ++# - LOGDEST ++# - MAKEFLAGS ++# - PACKAGER + load_vars() { + local makepkg_conf="$1" var + + [[ -f $makepkg_conf ]] || return 1 + + for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do +- [[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf") ++ [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf") + done + + return 0 + } + +-create_chroot() { +- # Lock the chroot we want to use. We'll keep this lock until we exit. +- lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy" ++# Usage: sync_chroot $CHROOTDIR/$CHROOT <$CHROOTCOPY|$copydir> ++sync_chroot() { ++ local chrootdir=$1 ++ local copy=$2 ++ local copydir='' ++ if [[ ${copy:0:1} = / ]]; then ++ copydir=$copy ++ else ++ copydir="$chrootdir/$copy" ++ fi ++ ++ if [[ "$chrootdir/root" -ef "$copydir" ]]; then ++ error 'Cannot sync copy with itself: %s' "$copydir" ++ return 1 ++ fi ++ ++ # Detect chrootdir filesystem type ++ local chroottype=$(stat -f -c %T "$chrootdir") + +- if [[ ! -d $copydir ]] || $clean_first; then + # Get a read lock on the root chroot to make + # sure we don't clone a half-updated chroot +- slock 8 "$chrootdir/root.lock" "Locking clean chroot" ++ slock 8 "$chrootdir/root.lock" \ ++ "Locking clean chroot [%s]" "$chrootdir/root" + +- stat_busy "Creating clean working copy [%s]" "$copy" ++ stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$chrootdir/root" "$copydir" + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then + if [[ -d $copydir ]]; then + btrfs subvolume delete "$copydir" >/dev/null || +@@ -105,14 +132,18 @@ + + # Drop the read lock again + lock_close 8 +- fi + + # Update mtime + touch "$copydir" + } + +-clean_temporary() { +- stat_busy "Removing temporary copy [%s]" "$copy" ++# Usage: delete_chroot $copydir ++delete_chroot() { ++ local copydir=$1 ++ # Detect chrootdir filesystem type ++ local chroottype=$(stat -f -c %T "$copydir") ++ ++ stat_busy "Removing chroot copy [%s]" "$copydir" + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then + btrfs subvolume delete "$copydir" >/dev/null || + die "Unable to delete subvolume %s" "$copydir" +@@ -127,9 +158,14 @@ + stat_done + } + ++# Usage: install_packages $copydir $pkgs... + install_packages() { ++ local copydir=$1 ++ local install_pkgs=("${@:2}") ++ declare -i ret=0 + local pkgname + ++ local install_pkg + for install_pkg in "${install_pkgs[@]}"; do + pkgname="${install_pkg##*/}" + cp "$install_pkg" "$copydir/$pkgname" +@@ -142,11 +178,19 @@ + rm "$copydir/$pkgname" + done + +- # If there is no PKGBUILD we are done +- [[ -f PKGBUILD ]] || exit $ret ++ return $ret + } + ++# Usage: prepare_chroot $copydir $HOME $repack $run_namcap ++# Globals: ++# - MAKEFLAGS ++# - PACKAGER + prepare_chroot() { ++ local copydir=$1 ++ local USER_HOME=$2 ++ local repack=$3 ++ local run_namcap=$4 ++ + $repack || rm -rf "$copydir/build" + + mkdir -p "$copydir/build" +@@ -193,12 +237,12 @@ + printf 'builduser:x:%d:100:builduser:/:/usr/bin/nologin\n' "$builduser_uid" >>"$copydir/etc/passwd" + chown -R "$builduser_uid" "$copydir"/{build,pkgdest,srcpkgdest,logdest,srcdest,startdir} + +- if [[ -n $MAKEFLAGS ]]; then ++ if [[ -n ${MAKEFLAGS:-} ]]; then + sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf" + echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf" + fi + +- if [[ -n $PACKAGER ]]; then ++ if [[ -n ${PACKAGER:-} ]]; then + sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf" + echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf" + fi +@@ -211,10 +255,25 @@ + chmod 440 "$copydir/etc/sudoers.d/builduser-pacman" + fi + ++ if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then ++ local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q') ++ local ins='[repo] ++SigLevel = Optional TrustAll ++Server = file:///repo ++' ++ sed -i "${line}i${ins//$'\n'/\\n}" "$copydir/etc/pacman.conf" ++ fi ++ + # This is a little gross, but this way the script is recreated every time in the + # working copy + { + printf '#!/bin/bash\n' ++ declare -f _chrootprepare ++ printf '_chrootprepare "$@"\n' ++ } > "$copydir/chrootprepare" ++ chmod +x "$copydir/chrootprepare" ++ { ++ printf '#!/bin/bash\n' + declare -f _chrootbuild + printf '_chrootbuild "$@" || exit\n' + +@@ -231,13 +290,19 @@ + chmod +x "$copydir/chrootbuild" + } + ++# Usage: download_sources $copydir $src_owner ++# Globals: ++# - SRCDEST + download_sources() { ++ local copydir=$1 ++ local src_owner=$2 ++ + local builddir="$(mktemp -d)" + chmod 1777 "$builddir" + + # Ensure sources are downloaded +- if [[ -n $SUDO_USER ]]; then +- sudo -u $SUDO_USER env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \ ++ if [[ $USER != $src_owner ]]; then ++ sudo -u $src_owner env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \ + makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o + else + ( export SRCDEST BUILDDIR="$builddir" +@@ -247,10 +312,10 @@ + (( $? != 0 )) && die "Could not download sources." + + # Clean up garbage from verifysource +- rm -rf $builddir ++ rm -rf "$builddir" + } + +-_chrootbuild() { ++_chrootprepare() { + # This function isn't run in makechrootpkg, + # so no global variables + +@@ -259,6 +324,7 @@ + shopt -s nullglob + + # XXX: Workaround makepkg disliking read-only dirs ++ rm -rf -- /srcdest/* /startdir/* + ln -sft /srcdest /srcdest_host/* + ln -sft /startdir /startdir_host/* + +@@ -288,15 +354,42 @@ + exit 1 + fi + +- sudo -u builduser makepkg "$@" ++ # Sync deps now, as networking may be disabled during _chrootbuild ++ cp /repo/repo.db /var/lib/pacman/sync/repo.db ++ sudo -u builduser makepkg "$@" --nobuild ++} ++ ++_chrootbuild() { ++ # This function isn't run in makechrootpkg, ++ # so no global variables ++ ++ . /etc/profile ++ export HOME=/build ++ shopt -s nullglob ++ ++ cd /startdir ++ ++ sudo -u builduser makepkg "$@" --noextract --noprepare + } + ++# Usage: move_products $copydir $owner ++# Globals: ++# - PKGDEST ++# - LOGDEST + move_products() { ++ local copydir=$1 ++ local src_owner=$2 ++ ++ local pkgfile + for pkgfile in "$copydir"/pkgdest/*; do + chown "$src_owner" "$pkgfile" + mv "$pkgfile" "$PKGDEST" ++ if [[ $PKGDEST != $PWD ]]; then ++ ln -sf "$PKGDEST/${pkgfile##*/}" . ++ fi + done + ++ local l + for l in "$copydir"/logdest/*; do + [[ $l == */logpipe.* ]] && continue + chown "$src_owner" "$l" +@@ -310,6 +403,9 @@ + } + # }}} + ++main() { ++init_variables ++ + orig_argv=("$@") + + while getopts 'hcur:I:l:nTD:d:' arg; do +@@ -375,30 +471,45 @@ + [[ -d $SRCPKGDEST ]] || SRCPKGDEST=$PWD + [[ -d $LOGDEST ]] || LOGDEST=$PWD + +-create_chroot ++# Lock the chroot we want to use. We'll keep this lock until we exit. ++lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy" ++ ++if [[ ! -d $copydir ]] || $clean_first; then ++ sync_chroot "$chrootdir" "$copy" ++fi + + $update_first && arch-nspawn "$copydir" \ + "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + pacman -Syu --noconfirm + +-[[ -n ${install_pkgs[*]} ]] && install_packages ++if [[ -n ${install_pkgs[*]:-} ]]; then ++ install_packages "$copydir" "${install_pkgs[@]}" ++ ret=$? ++ # If there is no PKGBUILD we have done ++ [[ -f PKGBUILD ]] || exit $ret ++fi + +-download_sources ++download_sources "$copydir" "$src_owner" + +-prepare_chroot ++prepare_chroot "$copydir" "$USER_HOME" "$repack" + + if arch-nspawn "$copydir" \ + --bind-ro="$PWD:/startdir_host" \ + --bind-ro="$SRCDEST:/srcdest_host" \ + "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ ++ /chrootprepare "${makepkg_args[@]}" && ++ arch-nspawn "$copydir" \ ++ --bind-ro="$PWD:/startdir_host" \ ++ --bind-ro="$SRCDEST:/srcdest_host" \ ++ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + /chrootbuild "${makepkg_args[@]}" + then +- move_products ++ move_products "$copydir" "$src_owner" + else + (( ret += 1 )) + fi + +-$temp_chroot && clean_temporary ++$temp_chroot && delete_chroot "$copydir" + + if (( ret != 0 )); then + if $temp_chroot; then +@@ -409,3 +520,4 @@ + else + true + fi ++} diff --git a/src/chroot-tools/mkarchroot.patch b/src/chroot-tools/mkarchroot.patch new file mode 100644 index 0000000..3882ec6 --- /dev/null +++ b/src/chroot-tools/mkarchroot.patch @@ -0,0 +1,67 @@ +--- mkarchroot.in 2016-04-15 17:38:00.221067734 -0400 ++++ mkarchroot.ugly 2016-05-09 22:36:18.284175885 -0400 +@@ -1,4 +1,6 @@ + #!/bin/bash ++# License: GNU GPLv2 ++# + # This program 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; version 2 of the License. +@@ -14,23 +16,29 @@ + + working_dir='' + ++files=() ++ + usage() { + echo "Usage: ${0##*/} [options] working-dir package-list..." + echo ' options:' + echo ' -C <file> Location of a pacman config file' + echo ' -M <file> Location of a makepkg config file' + echo ' -c <dir> Set pacman cache' ++ echo ' -f <file> Copy file from the host to the chroot' ++ echo ' -s Do not run setarch' + echo ' -h This message' + exit 1 + } + + orig_argv=("$@") + +-while getopts 'hC:M:c:' arg; do ++while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + C) pac_conf="$OPTARG" ;; + M) makepkg_conf="$OPTARG" ;; + c) cache_dir="$OPTARG" ;; ++ f) files+=("$OPTARG") ;; ++ s) nosetarch=1 ;; + h|?) usage ;; + *) error "invalid argument '%s'" "$arg"; usage ;; + esac +@@ -68,6 +76,16 @@ + chmod 0755 "$working_dir" + fi + ++for file in "${files[@]}"; do ++ mkdir -p "$(dirname "$working_dir$file")" ++ cp "$file" "$working_dir$file" ++done ++ ++_env=() ++while read -r varname; do ++ _env+=("$varname=${!varname}") ++done < <(declare -x | sed -r 's/^declare -x ([^=]*)=.*/\1/' | grep -i '_proxy$') ++env -i "${_env[@]}" \ + pacstrap -GMcd ${pac_conf:+-C "$pac_conf"} "$working_dir" \ + "${cache_dirs[@]/#/--cachedir=}" "$@" || die 'Failed to install all packages' + +@@ -75,7 +93,8 @@ + echo 'LANG=C' > "$working_dir/etc/locale.conf" + echo "$CHROOT_VERSION" > "$working_dir/.arch-chroot" + +-exec arch-nspawn \ ++exec "$(librelib chroot/arch-nspawn)" \ ++ ${nosetarch:+-s} \ + ${pac_conf:+-C "$pac_conf"} \ + ${makepkg_conf:+-M "$makepkg_conf"} \ + ${cache_dir:+-c "$cache_dir"} \ diff --git a/src/dagpkg b/src/dagpkg new file mode 100755 index 0000000..1989b4d --- /dev/null +++ b/src/dagpkg @@ -0,0 +1,222 @@ +#!/usr/bin/env bash +# +# dagpkg - create a directed graph of package dependencies and build +# them in topological order + +# Copyright (C) 2014 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2014 Michał Masłowski <mtjm@mtjm.eu> +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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 + +source libremessages +source "$(librelib conf)" + +# Source variables from libretools +load_files libretools +check_vars libretools FULLBUILDCMD || exit 1 +#check_vars libretools HOOKPREBUILD HOOKLOCALRELEASE || exit 1 # optional + +# Source variables from makepkg +load_files makepkg +check_vars makepkg CARCH || exit 1 + +# Globals: +# - temp_dir +# - log +# - name (sort of, it's also local to visit_pkgbuild() ) +# - prev +# - I +# - marks +# - various PKGBUILD variables: +# - pkgbase/pkgname +# - epoch/pkgver/pkgrel +# - arch +# - {,make,check}depends + +# End inmediately but print an useful message +trap_exit() { + local signal=$1; shift + local msg=("$@") + term_title "error!" + echo + error "(%s) %s (leftovers on %s)" \ + "${0##*/}" "$(print "${msg[@]}")" "${temp_dir}" + trap -- "$signal" + kill "-$signal" "$$" +} + +setup_traps trap_exit + +source_pkgbuild() { + # Source this PKGBUILD, if it doesn't exist, exit + if ! load_PKGBUILD &>/dev/null; then + error "No PKGBUILD in %s" "$PWD" + exit 1 + fi + + # Save resources + # This is intentionally less exhaustive than unset_PKGBUILD() + # XXX: document which things we actually *want* to not be unset. + unset pkgdesc license groups backup install md5sums sha1sums \ + sha256sums source options &>/dev/null + + unset build package &>/dev/null + + local _pkg + for _pkg in "${pkgname[@]}"; do + unset "package_${_pkg}" &>/dev/null || true + done + + # This is the name of the package + name="${pkgbase:-${pkgname[0]}}" +} + +source_pkgbuild + +# A temporary work dir and log file +temp_dir="${1:-$(mktemp -dt ${name}-testpkg-XXXX)}" +log="${temp_dir}/buildorder" + +# Mark array for DFS-based topological sort. See +# https://en.wikipedia.org/wiki/Topological_sort for an explanation of +# the algorithm. Key: package name, value: 0 for unvisited package, 1 +# during visit, 2 after visit. +declare -A marks + +# Visit a PKGBUILD for graph building. +visit_pkgbuild() { + # The name of the previous package + prev="${1}" + + local name + source_pkgbuild + + # If it's already built we don't bother + if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then + return + fi + + # Detect cycle or already visited package + case "${marks[$name]:-0}" in + 1) msg2 "cycle found with %s depending on %s" $prev $name + exit 1;; + 2) return;; + esac + + msg "%s (%s)" ${name} ${prev} + + if ! in_array "${CARCH}" "${arch[@]}"; then + warning "%s isn't ported to %s yet" ${name} ${CARCH} + fi + + # If the envvar I contains this package, ignore it and exit + if in_array "$name" $I; then + msg2 "%s ignored" ${name} + return + fi + + # Mark the package as being visited + marks[$name]=1 + + # Recurse into dependencies + local d + for d in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}"; do + # Cleanup dependency versions + d=$(echo $d | sed "s/[<>=].*//") + + # Where's the pkgbuild? + local w=$(toru-where $d) + + # Skip if not available + test -z "$w" && continue + + # Go to this dir + pushd $w &>/dev/null + + visit_pkgbuild "$name" + + popd &>/dev/null + done + + # Mark the package as finished + marks[$name]=2 + # Append it to the reversed list of packages to build. + echo "$name" >> "${log}" +} + +# If we specified a work dir on the cli it means we want to skip +# dependency graph creation and jump to build whatever is there +if [ -z "${1}" ]; then + # Visit the root PKGBUILD to make the graph. + visit_pkgbuild "" +else + msg "Resuming build..." +fi + +# enter work dir +pushd "${temp_dir}" &>/dev/null +nl ${log} | while read order pkg; do + # skip if already built + if test -f "${pkg}/built_ok"; then + warning "tried to build %s twice" "%{pkg}" + continue + fi + + # where's this package? + local w="$(toru-where "$pkg")" + test -z "$w" && continue + + # copy to work dir if not already + # this means you can make modifications to the pkgbuild during the + # graph build or remove the dir after a build failure and let dagpkg + # copy a new version + test -d "$pkg" || cp -r "$w" "$pkg" + pushd "$pkg" &>/dev/null + + term_title "%s(%s)" "$pkg" "$order" + + msg "Building %s" ${pkg} + + # upgrade the system + # this would probably have to go on HOOKPREBUILD if you're working + # outside chroots + sudo -E pacman -Syu --noconfirm + + # run the pre build command from libretools.conf + if [[ -n "$HOOKPREBUILD" ]]; then + ${HOOKPREBUILD} + fi + + # run the build command + ${FULLBUILDCMD} + + # Run local release hook with $1 = $repo + if [[ -n "$HOOKLOCALRELEASE" ]]; then + ${HOOKLOCALRELEASE} "$(basename "$(dirname "$w")")" + fi + + # it's built! + touch built_ok + + popd &>/dev/null +done + +popd &>/dev/null +# cleanup +rm -rf ${log} "${temp_dir}" + +term_title "done" diff --git a/src/devtools/.gitignore b/src/devtools/.gitignore new file mode 100644 index 0000000..d669bbd --- /dev/null +++ b/src/devtools/.gitignore @@ -0,0 +1,4 @@ +* +!Makefile +!.gitignore +!*.patch diff --git a/src/devtools/Makefile b/src/devtools/Makefile new file mode 100644 index 0000000..72907f0 --- /dev/null +++ b/src/devtools/Makefile @@ -0,0 +1,12 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +libretools-bins += checkpkg find-libdeps finddeps lddd +devtools-files = $(addsuffix .in,$(libretools-bins)) +am_sys_files += $(bindir)/find-libprovides + +$(DESTDIR)$(bindir)/find-libprovides: + install -d $(@D) + ln -sf find-libdeps $@ + +include $(topsrcdir)/automake.tail.mk diff --git a/src/devtools/checkpkg.patch b/src/devtools/checkpkg.patch new file mode 100644 index 0000000..5cfb794 --- /dev/null +++ b/src/devtools/checkpkg.patch @@ -0,0 +1,45 @@ +--- checkpkg.in 2014-03-21 13:59:31.849658036 -0400 ++++ checkpkg.ugly 2014-03-21 14:20:52.340291982 -0400 +@@ -1,8 +1,30 @@ + #!/bin/bash ++# License: Unspecified + + shopt -s extglob + +-m4_include(lib/common.sh) ++. "$(librelib messages)" ++ ++usage() { ++ print 'Usage: %s [-h]' "${0##*/}" ++ print 'Compare a locally built a package with the one in the repositories.' ++ echo ++ prose 'This should be run from a directory containing a ++ PKGBUILD. It searches for a locally built package ++ corresponding to the PKGBUILD, and downloads the last ++ version of that package from the pacman repositories. ++ It then compares the list of .so files provided by each ++ version of the package. It does this for each part of ++ a split package.' ++} ++ ++if [[ $1 = '-h' ]]; then ++ usage ++ exit 0 ++elif [[ $# -gt 0 ]]; then ++ usage >&2 ++ exit 1 ++fi + + # Source makepkg.conf; fail if it is not found + if [[ -r '/etc/makepkg.conf' ]]; then +@@ -17,7 +39,9 @@ + fi + + if [[ ! -f PKGBUILD ]]; then +- die 'This must be run in the directory of a built package.' ++ error 'This must be run in the directory of a built package.' ++ usage >&2 ++ exit 1 + fi + + . ./PKGBUILD diff --git a/src/devtools/find-libdeps.patch b/src/devtools/find-libdeps.patch new file mode 100644 index 0000000..1bcffc9 --- /dev/null +++ b/src/devtools/find-libdeps.patch @@ -0,0 +1,47 @@ +--- find-libdeps.in 2014-03-21 13:59:32.059649315 -0400 ++++ find-libdeps.ugly 2014-03-21 14:21:18.955744982 -0400 +@@ -1,6 +1,7 @@ + #!/bin/bash ++# License: Unspecified + +-m4_include(lib/common.sh) ++. "$(librelib messages)" + + set -e + shopt -s extglob +@@ -19,12 +20,32 @@ + *) die "Unknown mode %s" "$script_mode" ;; + esac + ++usage() { ++ print "Usage: find-lib(deps|provides) [options] <package file|extracted package dir>" ++ print "Find library dependencies or provides of a package." ++ echo ++ prose 'Prints a list of library dependencies in the format:' ++ echo ++ print ' <soname>=<soversion>-<soarch>' ++ echo ++ prose 'Where <soversion> is the shared library version, or ++ <soname> repeated if there is no version attached; and ++ <soarch> is the architecture of the library (either `32` ++ or `64`, based on the ELF Class).' ++ echo ++ print "Options:" ++ flag "--ignore-internal" "Ignore internal libraries; libraries ++ without a version attached" ++ flag "-h" "Show this message" ++} + if [[ -z $1 ]]; then +- echo "${0##*/} [options] <package file|extracted package dir>" +- echo "Options:" +- echo " --ignore-internal ignore internal libraries" ++ usage >&2 + exit 1 + fi ++if [[ $1 = '-h' ]]; then ++ usage ++ exit 0 ++fi + + if [[ -d $1 ]]; then + pushd $1 >/dev/null diff --git a/src/devtools/finddeps.patch b/src/devtools/finddeps.patch new file mode 100644 index 0000000..caf2121 --- /dev/null +++ b/src/devtools/finddeps.patch @@ -0,0 +1,35 @@ +--- finddeps.in 2014-03-21 13:59:32.249641424 -0400 ++++ finddeps.ugly 2014-03-21 14:21:09.949489174 -0400 +@@ -2,19 +2,26 @@ + # + # finddeps - find packages that depend on a given depname + # ++# License: Unspecified + +-m4_include(lib/common.sh) ++. "$(librelib messages)" + + match=$1 + ++usage() { ++ print 'Usage: %s <depname>' "${0##*/}" ++ print 'Find packages that depend on a given depname.' ++ echo ++ prose 'Run this script from the top-level directory of your ABS tree.' ++} + if [[ -z $match ]]; then +- echo 'Usage: finddeps <depname>' +- echo '' +- echo 'Find packages that depend on a given depname.' +- echo 'Run this script from the top-level directory of your ABS tree.' +- echo '' ++ usage >&2 + exit 1 + fi ++if [[ $match = '-h' ]]; then ++ usage ++ exit 0 ++fi + + find . -type d | while read d; do + if [[ -f "$d/PKGBUILD" ]]; then diff --git a/src/devtools/lddd.patch b/src/devtools/lddd.patch new file mode 100644 index 0000000..6383aa4 --- /dev/null +++ b/src/devtools/lddd.patch @@ -0,0 +1,29 @@ +--- lddd.in 2014-03-21 13:59:32.419634364 -0400 ++++ lddd.ugly 2014-03-21 14:21:31.538503947 -0400 +@@ -2,8 +2,25 @@ + # + # lddd - find broken library links on your machine + # ++# License: Unspecified + +-m4_include(lib/common.sh) ++. "$(librelib messages)" ++ ++usage() { ++ print "Usage: %s [-h]" "${0##*/}" ++ print "Find broken library links on your machine." ++ echo ++ prose 'Scans $PATH and library directories for ELF files with ++ references to missing shared libraries.' ++} ++ ++if [[ $1 = '-h' ]]; then ++ usage ++ exit 0 ++elif [[ $# -gt 0 ]]; then ++ usage >&2 ++ exit 1 ++fi + + ifs=$IFS + IFS="${IFS}:" diff --git a/src/gitget/Makefile b/src/gitget/Makefile new file mode 100644 index 0000000..2903f4a --- /dev/null +++ b/src/gitget/Makefile @@ -0,0 +1,4 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +include $(topsrcdir)/automake.tail.mk diff --git a/src/gitget/gitget b/src/gitget/gitget new file mode 100755 index 0000000..4d127c7 --- /dev/null +++ b/src/gitget/gitget @@ -0,0 +1,221 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +. libremessages + +# from makepkg +dir_is_empty() { + ( + shopt -s dotglob nullglob + files=("$1"/*) + (( ${#files} == 0 )) + ) +} + +# from makepkg +cd_safe() { + if ! cd "$1"; then + error "Failed to change to directory %s" "$1" + plain "Aborting..." + exit 1 + fi +} + +# from makepkg +download_git_checkout() { + local url=$1 + local ref=$2 + local dir=$3 + local name=$4 + local push=${5:-} + + if [[ ! -d "$dir/.git" ]] ; then + msg2 "Cloning %s %s repo..." "${name}" "git" + if ! git clone "$url" "$dir"; then + error "Failure while downloading %s %s repo" "${name}" "git" + plain "Aborting..." + exit 1 + fi + cd_safe "$dir" + if [[ -n $push ]]; then + git config remote.origin.pushUrl "$push" + fi + git checkout "$ref" + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then + if $FORCE; then + git config remote.origin.url "$url" + else + error "%s is not a clone of %s" "$dir" "$url" + plain "Aborting..." + exit 1 + fi + fi + if [[ -n $push ]] ; then + if $FORCE; then + git config remote.origin.pushUrl "$push" + else + local curpush="$(git config --get remote.origin.pushUrl)" + if [[ $? != 0 ]] ; then + error "%s does not have a %s configured" pushUrl "$name" + plain "Aborting..." + exit 1 + elif [[ $curpush != "$push" ]]; then + error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push" + plain "Aborting..." + exit 1 + fi + fi + fi + msg2 "Updating %s %s repo..." "${name}" "git" + if ! { git fetch --all -p && git checkout "$ref" && git pull origin "$ref"; } ; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${repo}" "git" + fi + fi +} + +# from makepkg +download_git_bare() { + local url=$1 + local dir=$2 + local name=$3 + local push=${4:-} + + if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then + msg2 "Cloning %s %s repo..." "${name}" "git" + if ! git clone --mirror "$url" "$dir"; then + error "Failure while downloading %s %s repo" "${name}" "git" + plain "Aborting..." + exit 1 + fi + if [[ -n $push ]]; then + cd_safe "$dir" + git config remote.origin.pushUrl "$push" + fi + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then + error "%s is not a clone of %s" "$dir" "$url" + plain "Aborting..." + exit 1 + fi + if [[ -n $push ]] ; then + if $FORCE; then + git config remote.origin.pushUrl "$push" + else + local curpush="$(git config --get remote.origin.pushUrl)" + if [[ $? != 0 ]] ; then + error "%s does not have a %s configured" pushUrl "$name" + plain "Aborting..." + exit 1 + elif [[ $curpush != "$push" ]]; then + error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push" + plain "Aborting..." + exit 1 + fi + fi + fi + msg2 "Updating %s %s repo..." "${name}" "git" + if ! git fetch --all -p; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${name}" "git" + fi + fi +} + +usage() { + print 'Usage: %s [OPTIONS] [bare|checkout] URL DIRECTORY' "${0##*/}" + print 'A URL-handler for git urls. Capable of updating or cloning.' + echo + prose "Clones or pulls from the git URL, to a local DIRECTORY. If + \`bare\` is specified, it will create a bare repository; if + \`checkout\` is specified, it will create or update a working tree." + echo + prose 'For a checkout, the tree to checkout is specified by appending + `#ANYTHING=TREE-ISH` to the URL. For example, `#branch=stable`, + or `#tag=v12.3`. Whatever is on the left side of the equal sign + is ignored, this is for compatibility with `makepkg` source + URLs.' + echo + prose "The URL may be prefixed with \`git+\`. This is also for + compatibility with \`makepkg\` source URLs." + echo + prose "It does safety checks, figures out whether to clone or pull, and + other helpful things. This exists because the same + \`download_git\` function from makepkg was being copied and + pasted again and again." + echo + print "Options:" + flag '-f' \ + 'Instead of checking to make sure configured URLs match, force + the update, and set the URLs.' + flag "-p $(_ URL)" \ + 'In addition to setting or checking `remotes.origin.url`, also + set or check `remotes.origin.pushUrl`' + flag "-n $(_ NAME)" \ + 'In messages, instead of using the basename of DIRECTORY as the + repository name, use NAME' + flag '-h' 'Show this message' +} + +FORCE=false +main() { + local push='' + local name='' + while getopts 'fp:n:h' flag; do + case "${flag}" in + f) FORCE=true;; + p) push=$OPTARG;; + n) name=$OPTARG;; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + [[ $# == 3 ]] || { usage >&2; return 1; } + local mode=$1 + local url=${2#git+} + local dir=$3 + + local urlmain=${url%%#*} + local urlfrag=${url#*#} + if [[ "$urlfrag" == "$urlmain" ]]; then + urlfrag='' + fi + local ref=${urlfrag#*=} + + if [[ -z $ref ]]; then + ref=master + fi + + name=${name:-${dir##*/}} + + case "$mode" in + checkout) download_git_checkout "$urlmain" "$ref" "$dir" "$name" "$push";; + bare) download_git_bare "$urlmain" "$dir" "$name" "$push";; + *) usage >&2; return 1;; + esac +} + +main "$@" diff --git a/src/gitget/libregit b/src/gitget/libregit new file mode 100755 index 0000000..e45c3a8 --- /dev/null +++ b/src/gitget/libregit @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +. libremessages + +usage() { + print 'Usage: %s REPO REF DIR' "${0##*/}" + print 'A compatibility wrapper around `gitget checkout`' + echo + prose "This exists because gitget used to be called libregit, and took + the arguments in this format, and I'm sure there are a few + scripts floating around that use it." + echo + prose "Clones or pulls from the git URL '<REPO>', and checks out the git + ref '<REF>' to the directory '<DIR>'." +} + +main() { + [[ $# == 3 ]] || { usage >&2; return 1; } + repo=$1 + ref=$2 + dir=$3 + + gitget checkout "${repo}#ref=${ref}" "${dir}" +} + +main "$@" diff --git a/src/is_built b/src/is_built new file mode 100755 index 0000000..29b2744 --- /dev/null +++ b/src/is_built @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +. libremessages + +usage() { + print "Usage: %s [-h] PKGNAME [PKGVER]" "${0##*/}" "${0##*/}" + print 'Detect iv a given package (version) is already in the repos' + echo + prose "If a version is specified, it assumed that you want a greater or + equal version." + echo + prose "Example usage:" + print " $ %s 'pcre' '20'" "${0##*/}" + echo + print "Exit status:" + print " 0: The package is built" + print " 1: The package has not built" + print " >1: There was an error" +} + +while getopts 'h' arg; do + case $arg in + h) usage; exit 0 ;; + *) usage >&2; exit 2 ;; + esac +done +if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then + usage >&2 + exit 2 +fi + +pkg=${1} +ver=${2:-0} +pver=$(LC_ALL=C pacman -Sddp --print-format '%v' "${pkg}" 2>/dev/null) + +# if pacman fails or returns nothing +r=$? + +result=$(vercmp "${pver}" "${ver}") +# result: +# -1 : pver < ver +# 0 : pver = ver +# 1 : pver > ver + +if [[ $result -ge 0 ]] && [[ $r -eq 0 ]]; then + exit 0 +else + exit 1 +fi diff --git a/src/lib/.gitignore b/src/lib/.gitignore new file mode 100644 index 0000000..650c85f --- /dev/null +++ b/src/lib/.gitignore @@ -0,0 +1,3 @@ +common.sh +common.sh.in +conf.sh diff --git a/src/lib/HACKING.md b/src/lib/HACKING.md new file mode 100644 index 0000000..8bebaf6 --- /dev/null +++ b/src/lib/HACKING.md @@ -0,0 +1,15 @@ +Special stuff about hacking ih the /src/lib directory: + + - Everything should be GPLv2 AND GPLv3 compatible. No GPLv3 only. + - Name a file `libre${NAME}` if it should be executable directly, or + `${name}.sh` if it should only be available to be sourced. + - When printing a message that is internal to /src/lib, and not part + of the programm calling the library; prefix the print command with + `_l`. `_l()` is defined in `common.sh` (and `librelib`, since it + cannot use any libraries itself). + - When changing the message functions, be aware that some are + duplicated in: + * /src/chroot-tools/chcleanup + * /src/chroot-tools/distcc-tool + * /src/lib/librelib + And that they probably need to be updated as well. diff --git a/src/lib/Makefile b/src/lib/Makefile new file mode 100644 index 0000000..9e9b4a8 --- /dev/null +++ b/src/lib/Makefile @@ -0,0 +1,45 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +libretools-libs += common.sh conf.sh +devtools-files = common.sh.in + +# Build ############################################################## + +$(outdir)/conf.sh: $(var)sysconfdir $(var)pkgconfdir + +$(outdir)/common.sh: $(outdir)/%: $(srcdir)/%.in $(srcdir)/%.head $(srcdir)/%.tail $(outdir)/Makefile + @echo "OUT $@" + @{ \ + cat '$(<D)/$*.head' && \ + echo && \ + sed -r \ + -e '/encoding problem/d;/LANG=/d' \ + -e 's/mesg=\$$(.)/mesg="$$(_ "$$\1")"/' \ + -e 's/gettext /_l _ /g' \ + -e "s/^(\s+)(msg|error) '/\1_l \2 '/" \ + -e 's|lock\(\)\s*\{|lock()\n{|' \ + '$(<D)/$*.in' && \ + echo && \ + cat '$(<D)/$*.tail' && \ + :; } > '$@' + +# Translate ########################################################## + +$(outdir)/blacklist.sh.pot: $(srcdir)/blacklist.sh $(srcdir)/librexgettext + @echo "OUT $@" + @{ \ + sed -n '/^# Usage:/,/()/{ /^#/ { =; p; } }' $< | \ + sed -r -e 's/^# (.*)/msgid "\1"\nmsgstr ""\n/' \ + -e 's|^[0-9]*$$|#. embedded usage text\n#: $<:&|' && \ + $(<D)/librexgettext --simple=_l:2 $< && \ + :; } | $(pofmt) > $@ +$(outdir)/common.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/conf.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/librelib.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/messages.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2 +$(outdir)/librexgettext.pot: LIBREXGETTEXT_FLAGS += --simple=errusage + +###################################################################### + +include $(topsrcdir)/automake.tail.mk diff --git a/src/lib/blacklist.sh b/src/lib/blacklist.sh new file mode 100644 index 0000000..0a3cc39 --- /dev/null +++ b/src/lib/blacklist.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# This may be included with or without `set -euE` + +# Copyright (C) 2013-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +# make sure XDG_CACHE_HOME is set +. "$(librelib conf)" + +# Usage: blacklist-normalize <$file +# Normalizes the syntax of the blacklist on stdin. +blacklist-normalize() { + sed -e '/^#/d' -e 's/^[^:]*$/&::/' -e 's/^[^:]*:[^:]*$/&:/' +} + +# Usage: blacklist-cat +# Prints the blacklist. +# Uses the cache, but downloads it if it doesn't exist. Also normalizes the blacklist for easier parsing. +blacklist-cat() { + local file="$XDG_CACHE_HOME/libretools/blacklist.txt" + if ! [[ -e $file ]]; then + # exit on failure, whether set -e or not + blacklist-update || return $? + fi + blacklist-normalize < "$file" +} + +# Usage: blacklist-update +# Updates (or creates) the cached copy of the blacklist. +blacklist-update() ( + . libremessages + load_files libretools || return 1 + check_vars libretools BLACKLIST || return 1 + + local remote_blacklist="$BLACKLIST" + local local_blacklist="$XDG_CACHE_HOME/libretools/blacklist.txt" + + _l stat_busy "Downloading blacklist of proprietary software packages" + + mkdir -p "${local_blacklist%/*}" + if wget -N -q -O "${local_blacklist}.part" "$remote_blacklist" 2>/dev/null; then + stat_done + mv -f "${local_blacklist}.part" "$local_blacklist" + else + stat_done + rm "${local_blacklist}.part" + if [[ -e "$local_blacklist" ]]; then + _l warning "Using local copy of blacklist" + else + _l error "Download failed, exiting" + return 1 + fi + + fi +) + +# Usage: blacklist-cat | blacklist-lookup $pkgname +# Filters to obtain the line for $pkgname from the blacklist on stdin. +# Exits successfully whether a line is found or not. +blacklist-lookup() { + local pkg=$1 + # we accept that $pkg contains no regex-nes + blacklist-normalize | grep "^$pkg:" || true +} + +# Usage: blacklist-cat | blacklist-get-pkg +# Prints only the package name field of the blacklist line(s) on stdin. +blacklist-get-pkg() { + blacklist-normalize | cut -d: -f1 +} + +# Usage: blacklist-cat | blacklist-get-rep +# Prints only the replacement package field of the blacklist line(s) on stdin. +blacklist-get-rep() { + blacklist-normalize | cut -d: -f2 +} + +# Usage: blacklist-cat | blacklist-get-reason +# Prints only the reason field of the blacklist line(s) on stdin. +blacklist-get-reason() { + blacklist-normalize | cut -d: -f3- +} diff --git a/src/lib/blacklist.sh.3.ronn b/src/lib/blacklist.sh.3.ronn new file mode 120000 index 0000000..9001445 --- /dev/null +++ b/src/lib/blacklist.sh.3.ronn @@ -0,0 +1 @@ +libreblacklist.1.ronn
\ No newline at end of file diff --git a/src/lib/common.sh.3.ronn b/src/lib/common.sh.3.ronn new file mode 100644 index 0000000..30003e0 --- /dev/null +++ b/src/lib/common.sh.3.ronn @@ -0,0 +1,16 @@ +common.sh -- common Bash routines from devtools +=============================================== + +## SYNOPSIS + +`. "$(librelib common)"` + +## DESCRIPTION + +In short, don't use this. Use `libremessages`(1) instead. +`libremessages` uses this internally. + +## SEE ALSO + + * librelib(7) + * libremessages(1)/messages.sh(3) diff --git a/src/lib/common.sh.head b/src/lib/common.sh.head new file mode 100644 index 0000000..23bfeb8 --- /dev/null +++ b/src/lib/common.sh.head @@ -0,0 +1,25 @@ +#!/hint/bash +# This may be included with or without `set -euE` + +# This file is included by libremessages. +# You should probably use libremessages instead of this. + +# License: Unspecified + +shopt -s extglob + +if [[ -z ${_INCLUDE_COMMON_SH:-} ]]; then +_INCLUDE_COMMON_SH=true + +[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools' +[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale' + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi + +_l() { + TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@" +} diff --git a/src/lib/common.sh.tail b/src/lib/common.sh.tail new file mode 100644 index 0000000..e133fad --- /dev/null +++ b/src/lib/common.sh.tail @@ -0,0 +1 @@ +fi diff --git a/src/lib/conf.sh.3.ronn b/src/lib/conf.sh.3.ronn new file mode 100644 index 0000000..0974bdb --- /dev/null +++ b/src/lib/conf.sh.3.ronn @@ -0,0 +1,126 @@ +conf.sh(3) -- easy loading of configuration files +================================================= + +## SYNOPSIS + +`. "$(librelib conf)"` + +## DESCRIPTION + +`conf.sh` is a Bash(1) library to easily load various configuration +files related to Arch Linux/Parabola(7) and libretools(7). + +### VARIABLES + +When loading configuration files in a program run with `sudo`(8), it +is often desirable to load the configuration files from the home +directory of the user who called `sudo`, instead of from /root. + +To accommodate this, instead of using the usual $<USER> and $<HOME>, +`conf.sh` sets $<LIBREUSER> and $<LIBREHOME>, which it then uses. + + * <LIBREUSER>: + If $<SUDO_USER> is set, then $<LIBREUSER> is set to + that. Otherwise, $<LIBREUSER> is set to the value of $<USER>. + * <LIBREHOME>: + If $<LIBREUSER> == $<USER>, then $<LIBREHOME> is set to the value + of $<HOME>. Otherwise, it is set to the default home directory + of the user $<LIBREUSER>. + +Further, `conf.sh` works with XDG; it sets and uses +$<XDG_CONFIG_HOME> and $<XDG_CACHE_HOME> with the following values: + + * <XDG_CONFIG_HOME>: + If it isn't already set, it is set to "$<LIBREHOME>/.config" and + exported. + * <XDG_CACHE_HOME>: + If it isn't already set, it is set to "$<LIBREHOME>/.cache" and + exported. + +Note that only the XDG_* variables are exported. + +### GENERIC ROUTINES + +The following routines take a "slug" to refer to a group of +configuration files; that is the basename of the files. For example, +<SLUG>='abs' will identify `/etc/abs.conf` and `$<LIBREHOME>/.abs.conf`. + +The routines you will likely actually use are: + + * `load_files` <SLUG>: + Loads the configuration files for <SLUG>, loading the files in + the correct order, and checking the appropriate environmental + variables. + * `check_vars` <SLUG> <VARS>...: + Checks to see if all of <VARS> are defined. If any of them + aren't, it prints a message telling the user to configure them in + the configuration files for <SLUG>, and returns with a non-zero + status. + * `get_var` <SLUG> <VAR> <DEFAULT>: + If <VAR> is set in the configuration for <SLUG>, print its + value, considering environmental variables. If it is not set, + return <DEFAULT>. This does NOT work for array variables. + * `set_var` <SLUG> <VAR> <VALUE>: + Set the variable <VAR> equal to <VALUE> in the configuration file + for <SLUG> of highest precedence that already exists and is + writable. If no files fit this description, the routine does + nothing and returns a non-zero exit status. This does NOT work + for array variables. + +There are two more routines the above routines use internally that are +used internally by by the above routines. You are unlikely to use +them directly, but they might be useful for debugging, or at least +describing behavior. + + * `list_files` <SLUG>: + Lists (newline-separated) the configuration files that must be + considered for <SLUG>. Files listed later take precedence over + files listed earlier. + * `list_envvars` <SLUG>: + Lists (newline-separated) the environmental variables that take + precedence over the settings in the configuration files for + <SLUG>. For example, in `makepkg.conf`(8) (<SLUG>=makepkg), if the + <PACKAGER> environmental variable is set, the value in the + configuration file is ignored. + +### PKGBUILD ROUTINES + +These two routines deal with loading `PKGBUILD`(5) files. + + * `unset_PKGBUILD`: + Unsets all variables and functions that might be set in a + `PKGBUILD`(5) file, including those specific to `librefetch`(8). + * `load_PKGBUILD` [<FILE>]: + "Safely" loads a PKGBUILD. Everything that it would normally set + is unset first, $<CARCH> is set according to `makepkg.conf`(5), + then the file is loaded. The file to be loaded is <FILE>, or + "./PKGBUILD" by default. This isn't safe, security wise, in that + the PKGBUILD is free to execute code. + +### SLUGS + +The differences in behavior for anything that takes a slug comes down +to the differences in the output for `list_files` and `list_envvars`. + +The "known" slugs are "abs", "makepkg", "libretools", and anything +beginning with "xbs". If anything else is given, then: + + * `list_files` will give back "/etc/libretools.d/<SLUG>.conf" and + "$<XDG_CONFIG_HOME>/libretools/<SLUG>.conf" + * `list_envvars` will give back an empty list. + +The rules for <SLUG>=(abs|makepkg|libretools) are more easily +expressed in code than prose, so it is recommended that you look at +that. + +## BUGS + +`get_var` and `set_var` do not work with arrays. + +## SEE ALSO + +librelib(7) + +abs.conf(5), makepkg.conf(5), libretools.conf(5), PKGBUILD(5) + +chroot.conf(5), librefetch.conf(5) diff --git a/src/lib/conf.sh.in b/src/lib/conf.sh.in new file mode 100644 index 0000000..8394801 --- /dev/null +++ b/src/lib/conf.sh.in @@ -0,0 +1,243 @@ +#!/hint/bash +# This may be included with or without `set -euE` + +# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +LIBREUSER="${SUDO_USER:-$USER}" +if [[ $LIBREUSER == $USER ]]; then + LIBREHOME=$HOME +else + eval "LIBREHOME=~$LIBREUSER" +fi +if [[ -z ${XDG_CONFIG_HOME:-} ]]; then + export XDG_CONFIG_HOME="${LIBREHOME}/.config" +fi +if [[ -z ${XDG_CACHE_HOME:-} ]]; then + export XDG_CACHE_HOME="${LIBREHOME}/.cache" +fi + +# Low-level generic functions ################################################## + +# Usage: list_files $slug +# Lists the configuration files to be considered for $slug. +# Later files should take precedence over earlier files. +list_files() { + local slug=$1 + local sysconfdir=${_librelib_conf_sh_sysconfdir:-@sysconfdir@} + local pkgconfdir=${_librelib_conf_sh_pkgconfdir:-@pkgconfdir@} + case $slug in + abs) + echo "${sysconfdir}/$slug.conf" + echo "$LIBREHOME/.$slug.conf" + ;; + makepkg) + local manual="${MAKEPKG_CONF:-}" + local system="${sysconfdir}/$slug.conf" + local olduser="$LIBREHOME/.$slug.conf" + local newuser="$XDG_CONFIG_HOME/pacman/$slug.conf" + if [[ "$manual" != "$system" && -r "$manual" ]]; then + # Manually-specified file + echo "$manual" + else + # Normal file lookup + echo "$system" + if [[ -r "$olduser" && ! -r "$newuser" ]]; then + echo "$olduser" + else + echo "$newuser" + fi + fi + ;; + xbs*) + echo "${sysconfdir}/xbs/$slug.conf" + echo "$XDG_CONFIG_HOME/xbs/$slug.conf" + ;; + libretools) + echo "${sysconfdir}/$slug.conf" + echo "$XDG_CONFIG_HOME/libretools/$slug.conf" + ;; + *) + echo "${pkgconfdir}/$slug.conf" + echo "$XDG_CONFIG_HOME/libretools/$slug.conf" + ;; + esac +} + +# Usage: list_envvars $slug +# Lists the environmental variables that take precedence over the configuration +# files for $slug. +list_envvars() { + local slug=$1 + case $slug in + makepkg) + printf '%s\n' \ + PKGDEST SRCDEST SRCPKGDEST LOGDEST \ + BUILDDIR \ + PKGEXT SRCEXT \ + GPGKEY PACKAGER \ + CARCH + ;; + libretools) + printf '%s\n' DIFFPROG + ;; + xbs) + printf '%s\n' BUILDSYSTEM + ;; + *) :;; + esac +} + +# High-level generic functions ################################################# + +# Usage: load_files $slug +# Loads the configuration files for $slug in the proper order. +load_files() { + local slug=$1 + local var + local file + + # Save the existing versions at _VARNAME + for var in $(list_envvars $slug); do + [[ -n ${!var:-} ]] && eval "_$var=\${$var}" + done + + # Load the files + for file in $(list_files $slug); do + if [[ -r $file ]]; then + . "$file" || return 1 + fi + done + + # Restore the _SAVED versions + for var in $(list_envvars $slug); do + eval "$var=\${_$var:-\${$var:-}}" + done +} + +# Usage: check_vars $slug VAR1 VAR2... +# Check whether the variables listed are properly set. +# If not, it prints a message saying to set them in the configuration file(s) +# for $slug. +check_vars() ( + local slug=$1; shift + + local ret=0 + + local VAR + for VAR in "$@"; do + if [[ -z ${!VAR:-} ]]; then + type print &>/dev/null || . libremessages + if [[ $(list_files $slug|wc -l) -gt 1 ]]; then + _l print "Configure '%s' in one of:" "$VAR" + list_files $slug | sed 's/./ -> &/' + else + _l print "Configure '%s' in '%s'" "$VAR" "$(list_files $slug)" + fi + ret=1 + fi + done >&2 + + if [[ $ret != 0 ]]; then + return 1 + fi +) + +# Usage: get_var <slug> <var_name> <default_value> +# Does not work with arrays +get_var() ( + set +euE + local slug=$1 + local setting=$2 + local default=$3 + load_files "$slug" + printf '%s' "${!setting:-${default}}" +) + +# Usage: set_var <slug> <var_name> <value> +# Does not work with arrays +set_var() { + local slug=$1 + local key=$2 + local val=$3 + local file + for file in $(list_files "$slug"|tac); do + if [[ -w $file ]]; then + sed -i "/^\s*$key=/d" "$file" + printf '%s=%q\n' "$key" "$val" >> "$file" + return 0 + fi + done + return 1 +} + +# PKGBUILD (not configuration, per se) ######################################### + +unset_PKGBUILD() { + # This routine is based primarily off of the PKGBUILD(5) man-page, + # version 4.2.0, dated 2014-12-31 + + # This is kinda weird, but everything is more readable with + # this as a utility function, but I didn't want to introduce a + # new global function, so I just introduced it with the name + # of a function that we get to unset anyway. So it can't + # clash with anything! + mksource() { + # For each arg, `unset -v` all variables matching ${arg} and ${arg}_* + local v + for v in "$@"; do + unset -v "$v" $(declare -p|sed -rn "s/^declare -\S+ (${v}_[a-zA-Z0-9_]*)=.*/\1/p") + done + } + + # This line is taken from the makepkg source + local known_hash_algos=('md5' 'sha1' 'sha224' 'sha256' 'sha384' 'sha512') + + # From the "OPTIONS AND DIRECTIVES" section (in order of mention) + unset -v pkgname pkgver + unset -f pkgver + unset -v pkgrel pkgdesc epoch url license install changelog + + mksource source + unset -v validpgpkeys noextract + local sums=("${known_hash_algos[@]/%/sums}") + mksource "${sums[@]}" + + unset -v groups arch backup + mksource depends makedepends checkdepends optdepends + mksource conflicts provides replaces + unset -v options + + # From the "PACKAGING FUNCTIONS" section (in order of mention) + unset -f package prepare build check + + # From the "PACKAGE SPLITTING" section + unset -f $(declare -f|sed -n 's/^\(package_\S*\) ()\s*$/\1/p') + unset -v pkgbase + + # These are used by the `librefetch` program + unset -v mksource mknoextract "${sums[@]/#/mk}" + unset -v mkdepends + unset -f mksource +} + +load_PKGBUILD() { + local file=${1:-./PKGBUILD} + unset_PKGBUILD + CARCH="$(get_var makepkg CARCH "`uname -m`")" + . "$file" +} diff --git a/src/lib/libreblacklist b/src/lib/libreblacklist new file mode 100755 index 0000000..6c354fe --- /dev/null +++ b/src/lib/libreblacklist @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Copyright (C) 2013-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +if [[ "${0##*/}" != libreblacklist ]]; then + . "$(librelib blacklist)" +else + set -euE + + lib_file="$(librelib blacklist)" + . "$lib_file" + + usage-outside() { + sed -n '/^# Usage:/,/()/p' "$lib_file" | + tr '\n' '\r' | sed 's/\s*()\s*[{(]/\n/g' + } + # The output format of this is: + # - The first line is "Usage:" + # - The second line is a brief description + # - The last line is the command name (prefixed with "blacklist-") + # - The in-between lines are the extended description. + usage-inside() { + sed 's/\r/\n/g'<<<"$1"|sed -e '/^$/d' -e 's/^# //' + } + + usage() { + export TEXTDOMAIN='librelib' + export TEXTDOMAINDIR='/usr/share/locale' + . "$(librelib messages)" + if [[ $# -eq 0 ]]; then + print "Usage: %s [-h] COMMAND [ARGUMENTS]" "${0##*/}" + print "Tool for working with the nonfree software blacklist" + echo + print "Commands:" + usage-outside | while read -r sec; do sec="$(usage-inside "$sec")" + cmd=$(<<<"$sec" sed -n '$s/^blacklist-//p') + desc="$(_ "$(sed -n 2p <<<"$sec")")" + flag "$cmd" "${desc//blacklist-/${0##*/} }" + done + else + usage-outside | while read -r sec; do sec="$(usage-inside "$sec")" + cmd=$(<<<"$sec" sed -n '$s/^blacklist-//p') + if [[ "$cmd" == "$1" ]]; then + <<<"$sec" sed '$d' | + while read -r line; do print "$line"; done | + sed "s/blacklist-/${0##*/} /g" | + fmt -us + return 0 + fi + done + fi + } + + main() { + if [[ $# -eq 0 ]]; then + usage >&2 + exit 1 + fi + _blacklist_cmd=$1 + shift + if [[ $_blacklist_cmd == -h ]]; then + usage "$@" + else + "blacklist-$_blacklist_cmd" "$@" + fi + } + + main "$@" +fi diff --git a/src/lib/libreblacklist.1.ronn b/src/lib/libreblacklist.1.ronn new file mode 100644 index 0000000..d56eb60 --- /dev/null +++ b/src/lib/libreblacklist.1.ronn @@ -0,0 +1,23 @@ +libreblacklist(1) -- Tools for working with the your-freedom blacklist +====================================================================== + +## SYNOPSIS + +`. "$(librelib blacklist)"`<br> +`. libreblacklist`<br> +`libreblacklist` [-h] <COMMAND> [<ARGS>...] + +## DESCRIPTION + +`libreblacklist` is a set of tools for working with the your-freedom +blacklist. + +It may be included into a `Bash`(1) program as a library, which will +expose it's routines as "blacklist-<COMMAND>", or it may be invoked on +the command line as "libreblacklist <COMMAND>". + +See `libreblacklist -h` for more information. + +## SEE ALSO + +librelib(7) diff --git a/src/lib/librelib b/src/lib/librelib new file mode 100755 index 0000000..e9d184e --- /dev/null +++ b/src/lib/librelib @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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_libdir=/usr/lib/libretools + +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi + +_l() { + TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@" +} + +print() { + local mesg="$(_ "$1")" + shift + printf -- "$mesg\n" "$@" +} + +whitespace_collapse() { + tr '\n' '\r' | sed -r \ + -e 's/\r/ /g' -e 's/\t/ /g' \ + -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' +} + +prose() { + local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift + printf -- "$mesg" "$@" | fmt -u +} + +cmd=${0##*/} +usage() { + . libremessages + print 'Usage: . $(%s LIBRARY)' "$cmd" + print 'Usage: %s -h' "$cmd" + print "Finds a Bash library file" + echo + prose "While some libraries can be sourced just by their name because + they are installed in PATH (like libremessages), some are not + installed there (like conf.sh), so a path must be given. + Hardcoding that path is the way of the dark side." + echo + prose 'By default, it looks for the files in `%s`, but this can be + changed with the environmental variable LIBRETOOLS_LIBDIR.' "$default_libdir" + echo + print "Example usage:" + printf ' . $(%s conf)\n' "$cmd" + echo + print "Options:" + flag '-h' 'Show this message' +} + +main() { + if [[ $# != 1 ]]; then + _l usage >&2 + return 2 + fi + if [[ $1 == '-h' ]]; then + _l usage + return 0; + fi + + if [[ -z $LIBRETOOLS_LIBDIR ]]; then + export LIBRETOOLS_LIBDIR=$default_libdir + fi + + lib=$1 + lib=${lib#libre} + lib=${lib%.sh} + + for file in ${lib} libre${lib} ${lib}.sh libre${lib}.sh; do + if [[ -f "$LIBRETOOLS_LIBDIR/$file" ]]; then + printf '%s\n' "$LIBRETOOLS_LIBDIR/$file" + return 0; + fi + done + _l print '%s: could not find library: %s' "$cmd" "$lib" >&2 + return 1 +} + +main "$@" diff --git a/src/lib/librelib.1.ronn b/src/lib/librelib.1.ronn new file mode 100644 index 0000000..fe64e92 --- /dev/null +++ b/src/lib/librelib.1.ronn @@ -0,0 +1,55 @@ +librelib(1) -- finds a Bash library file +======================================== + +## SYNOPSIS + +`. "$(librelib LIBRARY)"`<br> +`librelib -h` + +## DESCRIPTION + +`librelib` is a program to find a Bash(1) library file at run-time. +This way, the path does not need to be hard-coded into the +application; think of it as a sort of dynamic-linker for shell +programs. + +There are several reasons for doing this, instead of hard-coding the +path: + + * The install path can change in the future without having to change + programs that use them. + * The install directory can be configured at runtime, by setting + `LIBRETOOLS_LIBDIR`, similar to `LD_PRELOAD` (this is used when + running the test suite). + * The naming scheme of a library can change (such as between + `libreNAME` and `NAME.sh` without changing programs that use it. + +By default, `librelib` looks in `/usr/lib/libretools`, but that can be +changed by setting the `LIBRETOOLS_LIBDIR` environmental variable to +the directory it should look in. + +When searching for a library, `librelib` first strips `libre` from the +beginning of the name, and `.sh` from the end. This means that all of +the following are equivalent: + + . "$(librelib messages)" + . "$(librelib messages.sh)" + . "$(librelib libremessages)" + . "$(librelib libremessages.sh)" + +Once it has the 'base' name of the library it is looking for, it looks +for a file with that 'base' name (allowing for, but not requiring +`libre` to be prepended, or `.sh` to be appended) in whichever +directory it is looking in. + +If it cannot find a suitable library file, it will print an error +message to standard error, and exit with a code of 1. + +## Examples + + . "$(librelib messages)" + . "$(librelib conf)" + +## SEE ALSO + +librelib(7) diff --git a/src/lib/librelib.7.ronn b/src/lib/librelib.7.ronn new file mode 100644 index 0000000..33b0c55 --- /dev/null +++ b/src/lib/librelib.7.ronn @@ -0,0 +1,49 @@ +librelib(7) -- Suite of Bash libraries +====================================== + +## SYNOPSIS + +Overview of the librelib Bash library suite. + +## DESCRIPTION + +There are three parts to librelib: + + 1. The `librelib`(1) executable. + 2. The non-executable libraries installed in `/usr/lib/libretools` + 3. The executable libraries installed in `/usr/bin` + +The `librelib` executable isn't very exciting, it just finds the +libraries installed in `/usr/lib/libretools`. Think of it as a sort +of dynamic-linker. + +The 'core' of librelib are the libraries installed in +`/usr/lib/libretools`. These are `Bash`(1) libraries that may be +sourced in Bash programs. + +Some of these libraries also make sense as stand-alone programs, where +if they are invoked directly, the first argument is the library +routine to be executed. For example, the `messages` library may be +included, or executed: + + . "$(librelib messages)" + msg2 "Foo was found: %s" "$foo" + # or + libremessages msg2 "Foo was found: %s" "$foo" + +The `blacklist` library is similar: + + . "$(librelib blacklist)" + blacklist-update + # or + libreblacklist update + + + +## SEE ALSO + + * librelib(1) + * libremessages(1)/messages.sh(3) + * libreblacklist(1)/blacklist.sh(3) + * conf.sh(3) + * common.sh(3) diff --git a/src/lib/libremessages b/src/lib/libremessages new file mode 100755 index 0000000..647204a --- /dev/null +++ b/src/lib/libremessages @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright (C) 2012-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +if [[ "${0##*/}" != libremessages ]]; then + . "$(librelib messages)" +else + set -euE + . "$(librelib messages)" + "$@" +fi diff --git a/src/lib/libremessages.1.ronn b/src/lib/libremessages.1.ronn new file mode 100644 index 0000000..d4fac85 --- /dev/null +++ b/src/lib/libremessages.1.ronn @@ -0,0 +1,241 @@ +libremessages(1) -- common Bash routines +======================================== + +## SYNOPSIS + +`. "$(librelib messages)"`<br> +`. libremessages`<br> +`libremessages` <COMMAND> + +## DESCRIPTION + +`libremessages` is a shell library containing many common routines. +The name is a bit of a misnomer, it mostly deals with printing +messages, but also has other things. + +`libremessages` uses `common.sh`(3) internally for a large portion of +it's functionality. The authors make no promises that functionality +that is implemented in `libremessages` won't move into `common.sh` or +vice-versa. So, it is recommended that you use `libremessages`, not +`common.sh`. + +### STAND ALONE USAGE + +The "normal" way to use libremessages is to source it, then call the +provided routines. + +However, if you call libremessages directly, the first argument is +taken as the function to call, and the remaining arguments are passed +to it. The only cases where this doesn't work are the lockfile +routines (`lock`, `slock`, and `lock_close`), because lockfiles are +managed as file descriptors. + +### VARIABLES + +The following variables for printing terminal color codes are set: +`ALL_OFF`, `BOLD`, `BLUE`, `GREEN`, `RED`, `YELLOW`. If standard +error is not a terminal (see `isatty`(3)), they are set, but empty. +They are marked as readonly, so it is an error to try to set them +afterwords. + +### MESSAGE FORMAT + +All routines feed the message/format string through `gettext`(1), if +it is available. + +The descriptions will frequently reference `printf`(1)--this isn't +really that `printf`. The program described by the manual page +`printf`(1) is probably the version from GNU coreutils, every time it +is used here, it is `bash`(1)'s internal implementation; try running +the command `help printf` from a Bash shell for more information. + +### GENERAL ROUTINES + +Unless otherwise noted, these do not implicitly call `gettext`. + + * `_` <MESSAGE>: + If `gettext` is available, calls `gettext`, otherwise just prints + the arguments given. + + * `in_array` <NEEDLE> <HAYSTACK>...: + Evaluates whether <HAYSTACK> includes <NEEDLE>; returns 0 if it + does, non-zero if it doesn't. + + * `panic`: + For the times when you can't reasonably continue, similar to + "assert" in some programming languages. + + * `setup_traps` [<HANDLER>]: + Sets traps on TERM, HUP, QUIT and INT signals, as sell as the ERR + event, similar to makepkg. If <HANDLER> is specified, instead of + using the default handler (which is good for most purposes), it + will call <HANDLER> with the arguments + `<HANDLER> <SIGNAL_NAME> <MESSAGE> [<MESSAGE_ARGS>...]`, where + <MESSAGE> is a `printf`(1)-formatted string that is fed through + `gettext`, and <MESSAGE_ARGS> are its arguments. + + * `whitespace_collapse`: + Collapses whitespace on stadard I/O, similar to HTML whitespace + collapsing, with the exception that it puts two spaces between + sentences. It considers newline, tab, and space to be + whitespace. + +### PROSE ROUTINES + +These routines print to standard output, and are useful for printing +word-wrapped prose. + +For each of these, <MESSAGE> is fed through `gettext` automatically. + +To generate gettext `.pot` files for programs using these routines, +plain `xgettext`(1) won't work because it doesn't know about +word-wrapping. Instead, you should use the `librexgettext`(1) program +which knows how do handle word-wrapping, and knows about each of these +routines by default. + + * `prose` <MESSAGE> [<ARGS>...]: + Takes a `printf`(1)-formatted string, collapses whitespace + (HTML-style), and then word-wraps it. + + * `bullet` <MESSAGE> [<ARGS>...]: + Similar to `prose`, but prints a bullet point before the first + line, and indents the remaining lines. + + * `flag` [<FLAG> <DESCRIPTION>|<HEADING>:]...: + Print a flag and description formatted for `--help` text. For + example:<br> + `flag '-N' 'Disable networking in the chroot'`<br> + The description is fed through `gettext`, the flag is not, so if + part of the flag needs to be translated, you must do that + yourself:<br> + `flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf'`<br> + Newlines in the description are ignored; it is + whitespace-collapsed (so newlines are stripped), then it is + re-word-wrapped, in the same way as `prose` and `bullet`. If you + pass in multiple flag/description pairs to the same invocation, + the descriptions are all aligned together. The ability to do + insert headings without resetting the alignment is the motivation + for also allowing headings to be in the list. In order to tell + the difference between a flag and a heading, a heading must end + with a colon (':'), and a flag must not. + +### NOTIFICATION ROUTINES + +These routines print to standard error, and all take arguments in the +same format as `printf`(1), except for `stat_done`, which doesn't take +any arguments. Each of these print to stderr, not stdout. + +For each of these, <MESSAGE> is fed through `gettext` automatically. + +To generate gettext `.pot` files for programs using these routines, +plain `xgettext`(1) will work if given correct flags, but you'll find +it easier to use `librexgettext`(1), which will handle each of these +without any extra flags. + + * `plain` <MESSAGE> [<ARGS>...]: + Prints a "plain" message in bold, indented with 4 spaces. + + * `msg` <MESSAGE> [<ARGS>...]: + Prints a top-level priority notification. + + * `msg2` <MESSAGE> [<ARGS>...]: + Prints a secondary notification. + + * `warning` <MESSAGE> [<ARGS>...]: + Prints a warning. + + * `error` <MESSAGE> [<ARGS>...]: + Prints an error message. + + * `stat_busy` <MESSAGE> [<ARGS>...]: + Prints a "working..." type message without a trailing newline. + + * `stat_done`: + Prints a "done" type message to terminate `stat_busy`. + + * `print` <MESSAGE> [<ARGS>...]: + Like `printf`(1), but `gettext`-aware, and automatically prints a + trailing newline. + + * `term_title` <MESSAGE> [<ARGS>...]: + Sets the terminal title to the specified message. + +### TEMPORARY DIRECTORY MANAGEMENT + +These are used by devtools, and not used within the rest of +libretools. + +They work by creating and removing the directory referred to by the +variable $<WORKDIR>; `libretools.conf`(5) uses the same variable to +where the user saves all their work. If you aren't careful with +these, you could end up deleting a lot of someone's work. + + * `setup_workdir`: + Creates a temporary directory, and sets the environmental + variable $<WORKDIR> to it. Also sets traps for the signals INT, + QUIT, TERM and HUP to run `abort`; and EXIT to run `cleanup` + (see `signal`(7)). + + * `cleanup` [<EXIT_STATUS>]: + *If* `setup_workdir` has been run, `rm -rf "$WORKDIR"`. If given + a numeric argument, it will then call `exit`(1) with that + argument, otherwise it calls `exit`(1) with a status of 0. + + * `abort`: + Calls `msg` with the message "Aborting...", then calls + `cleanup 255`. + + * `die` <MESSAGE> [<ARGS>...]: + Exactly like `error`, but calls `cleanup` and calls `exit`(1) + with a status of 255. + +### LOCKFILE ROUTINES + + * `lock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]: + Opens (creating if necessary) the file <LOCKFILE> with file + descriptor <FD> in the current process, and gets an exclusive + lock on it. If another program already has a lock on the file, + and this program needs to wait for the lock to be released, then + it uses `stat_busy`/`stat_done` to print <MESSAGE>. + + * `slock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]: + Identical like `lock`, but opens a shared lock. This is also + known as a "read lock". Many programs can have a shared lock at + the same time, as long as no one has an exclusive lock on it. + + * `lock_close` <FD>: + Closes file descriptor <FD>, releasing the lock opened on it. + +### MAKEPKG ROUTINES + +These routines relate to `makepkg`(8). + + * `find_cached_package` <PKGNAME> <PKGVER>[-<PKGREL] <ARCH>: + Searches for a locally built copy of the specified package, in + <PKGDEST> and the current working directory. If <PKGREL> is not + specified, any value will match. If multiple matching files are + found (not counting duplicate links), then an error is printed to + stderr and nothing is printed to stdout. + + * `get_full_version` [<PKGNAME>]: + Inspects variables that are set, and prints the full version + spec, including <epoch> if necessary, <pkgver>, and <pkgrel>. By + default, it will print the information for <pkgbase>, following + the normal rules for finding <pkgbase>. If <PKGNAME> is given, + it will print the information for that sub-package. The versions + for different parts of a split package don't have to be the same! + +## BUGS + +`term_title` currently only knows about the terminals screen, tmux, +xterm and rxvt (and their various <TERM> values; +"rxvt-unicode-256color" is still rxvt). + +## SEE ALSO + +librexgettext(1), librelib(7), gettext(1), common.sh(3) + +Things that were mentioned: + +bash(1), xgettext(1), exit(1), isatty(3), libretools.conf(5), +makepkg(8), printf(1), signal(7) diff --git a/src/lib/librexgettext b/src/lib/librexgettext new file mode 100755 index 0000000..db575d6 --- /dev/null +++ b/src/lib/librexgettext @@ -0,0 +1,226 @@ +#!/usr/bin/env bash +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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 TEXTDOMAIN='librelib' +export TEXTDOMAINDIR='/usr/share/locale' + +default_simple=( + --keyword={eval_,}{gettext,'ngettext:1,2'} + --keyword={_,print,term_title} + --keyword={msg,msg2,warning,error,stat_busy,die} + --keyword={lock,slock}:3 +) +default_prose=(--keyword={prose,bullet}) + +readonly default_simple default_prose + +if ! type gettext &>/dev/null; then + gettext() { echo "$@"; } +fi + +errusage() { + if [[ $# -gt 0 ]]; then + fmt="$(gettext "$1")"; shift + printf "${0##*/}: $fmt\n" "$@" + fi + usage >&2 +} + +usage() { + . libremessages + print 'Usage: %s [OPTIONS] FILES...' "${0##*/}" + print 'Generates .pot files for programs using libremessages' + echo + prose 'In librexgettext, there are 2 types of keywords:' + bullet 'simple: Simple keywords are just like normal xgettext' + bullet 'prose: Prose keywords are similar, but the text is + word-wrapped' + prose 'The keyword format is the same as in GNU xgettext.' + echo + prose 'The libremessages `flag` command is also handled + specially, and is not configurable as a keyword.' + echo + prose 'The default simple keywords are: %s' "${default_simple[*]#--keyword=}" + echo + prose 'The default prose keywords are: %s' "${default_prose[*]#--keyword=}" + echo + print 'Options:' + flag \ + '--simple=KEYWORD' 'Look for KEYWORD as an additional simple keyword' \ + '--prose=KEYWORD' 'Look for KEYWORD as an additional prose keyword' \ + '-k' 'Disable using the default keywords' \ + '-h, --help' 'Show this text' +} + +xgettext-sh() { + xgettext --omit-header --from-code=UTF-8 -L shell -k -o - "$@" +} + +xgettext-flag() { + { + # Stage 1: Generate + # + # Get all of the arguments to `flag`. Because `flag` + # takes an arbitrary number of arguments, just iterate + # through arg1, arg2, ... argN; until we've come up + # empty 3 times. Why 3? Because each flag takes 2 + # arguments, and because we don't keep track of which + # one of those we're on, waiting for 3 empties ensures + # us that we've had a complete "round" with nothing. + # + # Why can't I just do i+=2, and not have to keep track + # of empties? Because, we also allow for arguments + # ending in a colon to be headings, which changes the + # offsets. + declare -i empties=0 + declare -i i + for (( i=1; empties < 3; i++ )); do + local out + out="$(xgettext-sh --keyword="flag:$i,\"$i\"" "$@")" + if [[ -n $out ]]; then + printf -- '%s\n' "$out" + else + empties+=1 + fi + done + } | whitespace-collapse | sed '/^\#, sh-format/d' | { + # Stage 2: Parse + # + # Read in the lines, and group them into an array of + # (multi-line) msgs. This just makes working with + # them easier. + local msgs=() + declare -i i=-1 + local re='^#\. ([0-9]+)$' + IFS='' + local line + while read -r line; do + if [[ $line =~ $re ]]; then + i+=1 + fi + msgs[$i]+="$line"$'\n' + done + # Stage 3: Sort + # + # Now, we have the `msgs` array, and it is + # sorted such that it is all of the arg1's to `flag`, + # then all of the arg2's, then all of the arg3's, and + # so on. We want to re-order them such that it's all + # of the args for the first invocation then all of the + # args for the second; and so on. + # + # We do this by simply sorting them by the location + # that they appear in the file. Then, when we see the + # argument number go back down to 1, we know that a + # new invocation has started! + IFS=$'\n' + local locations=($( + local i + for i in "${!msgs[@]}"; do + declare -i arg row + local lines=(${msgs[$i]}) + arg=${lines[0]#'#. '} + row=${lines[1]##*:} + printf '%d.%d %d\n' "$row" "$arg" "$i" + done | sort -n + )) + # Stage 4: Output + # + # Now, we prune out the arguments that aren't + # localizable. Also, remove the "#." comment lines. + # As explained above (in stage 3), when we see $arg go + # to 1, that's the beginning of a new invocation. + local expectflag=true + local location + for location in "${locations[@]}"; do + IFS=' .' + local row arg i + read -r row arg i <<<"$location" + local msg="${msgs[$i]#*$'\n'}" + # Now we operate based on $row, $arg, and $msg + if [[ $arg == 1 ]]; then + expectflag=true + fi + if $expectflag; then + IFS=$'\n' + local lines=(${msg}) + if [[ ${lines[1]} == *':"' ]]; then + # We expected a flag, but got + # a heading + printf -- '%s\n' "$msg" + else + # We expected a flag, and got + # one! + expectflag=false + fi + else + printf -- '%s\n' "$msg" + expectflag=true + fi + done + } +} + +whitespace-collapse() { + tr '\n' '\r' | sed 's/"\r\s*"//g' | tr '\r' '\n' | # This removes the awkward word-wrapping done by xgettext + sed -r -e 's/(\\n|\\t|\t)/ /g' -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' # This collapses whitespace +} + +main() { + local simple=() + local prose=() + local files=() + local use_defaults=true + local error=false + + declare -i i + for (( i=1; i <= $#; i++ )); do + case "${!i}" in + --simple) i+=1; simple+=(--keyword="${!i}");; + --simple=*) simple+=(--keyword="${!i#*=}");; + --prose) i+=1; prose+=(--keyword="${!i}");; + --prose=*) prose+=(--keyword="${!i#*=}");; + -k) use_defaults=false;; + --help|-h) usage; return 0;; + --) i+=1; break;; + -*) errusage "unrecognized option: %s" "${!i}"; error=true;; + *) files+=("${!i}");; + esac + done + files+=("${@:$i}") + if [[ ${#files[@]} -lt 1 ]]; then + errusage "no input file given" + error=true + fi + if "$error"; then + return 1 + fi + if "$use_defaults"; then + simple+=("${default_simple[@]}") + prose+=("${default_prose[@]}") + fi + + # Main code + { + xgettext-sh "${simple[@]}" -- "${files[@]}" + xgettext-sh "${prose[@]}" -- "${files[@]}" | whitespace-collapse + xgettext-flag -- "${files[@]}" + } | sed '/^\#, sh-format/d' | msguniq -Fi --to-code=UTF-8 +} + +main "$@" diff --git a/src/lib/librexgettext.1.ronn b/src/lib/librexgettext.1.ronn new file mode 100644 index 0000000..de6abde --- /dev/null +++ b/src/lib/librexgettext.1.ronn @@ -0,0 +1,18 @@ +librexgettext(1) -- Extrct libremessages gettext strings from sources +===================================================================== + +## SYNOPSIS + +`librexgettext` [OPTIONS] FILES<br> +`librexgettext` [-h|--help] + +## DESCRIPTION + +`librexgettext` is a tool for extracting gettext strings from programs +using the fancy word-wrapping utilities of `libremessages`(1). + +See `librexgettext --help` for more information. + +## SEE ALSO + +libremessages(1) diff --git a/src/lib/messages.sh b/src/lib/messages.sh new file mode 100644 index 0000000..0125003 --- /dev/null +++ b/src/lib/messages.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash +# This may be included with or without `set -euE` + +# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For just the setup_traps() function: +# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2005 Aurelien Foret <orelien@chez.com> +# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +################################################################################ +# Inherit most functions from devtools # +################################################################################ + +. "$(librelib common.sh)" + +################################################################################ +# Own functions # +################################################################################ + +# Usage: panic +# +# For programming errors, bails immediately with little fanfare. +panic() { + echo "$(_l _ 'panic: malformed call to internal function')" >&2 + exit 1 +} + +# Usage: print MESG [ARGS...] +# +# Like printf, but gettext-aware, and prints a trailing newline +print() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$1")" + shift + printf -- "$mesg\n" "$@" +} + +# Usage: whitespace_collapse <<<STRING +# +# Collapses whitespace on stadard I/O, similar to HTML whitespace +# collapsing, with the exception that it puts two spaces between +# sentences. It considers newline, tab, and space to be whitespace. +whitespace_collapse() { + [[ $# == 0 ]] || panic + + tr '\n' '\r' | sed -r \ + -e 's/\r/ /g' -e 's/\t/ /g' \ + -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' +} + + +# Usage: prose MESG [ARGS...] +# +# Do HTML-style whitespace collapsing on the first argument, translate +# it (gettext), then word-wrap it to 75 columns. This is useful for +# printing a paragraph of prose in --help text. +prose() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift + printf -- "$mesg" "$@" | fmt -u +} + +# Usage: bullet MESG [ARGS...] +# Like prose, but print a bullet "-" before the first line, and indent the +# remaining lines. +bullet() { + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift + # Wrap the text to 71 columns; 75 (the default) minus a 4 column indent + printf -- "$mesg" "$@" | fmt -u -w 71 | sed -e '1s/^/ - /' -e '2,$s/^/ /' +} + +# Usage: flag [FLAG DESCRIPTION|HEADING:]... +# +# Print a flag and description formatted for --help text. +# +# ex: flag '-C <FILE>' 'Use this file instead of pacman.conf' +# +# The descriptions and headings are fed through gettext, the flags ar +# not, so if part of a flag needs to be translated, you must do that +# yourself: +# +# ex: flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf' +# +# If you want to line-break the description in the source, so it isn't +# crazy-long, feel free, it is reflowed/wrapped the same way as prose +# and bullet. If you pass in multiple flag/description pairs at once, +# the descriptions are all alligned together. +# +# A heading MUST end with a colon (':'), this is how it knows that it +# is a heading. Similarly, a flag MUST NOT end with a colon. +flag() { + local args=("$@") + + declare -i flaglen=0 + while [[ $# -gt 0 ]]; do + if [[ $1 == *: ]]; then + shift 1 + else + if [[ ${#1} -gt $flaglen ]]; then + flaglen=${#1} + fi + shift 2 + fi + done + set -- "${args[@]}" + + # Unless the $flaglen is extra-wide, the $desc should start at + # column 16 (that is two literal-tabs). If $flaglen is wide, + # this should be increased in increments of 8 (that is, a + # literal-tab). Everything should be wrapped to 75 columns. + + # The printf-format we use has 4 spaces built into it (two at + # the beginning, and two for a seperator). Therefore, the + # default indent is 16-4=12 columns. And the width left for + # $desc is (75-4)-indent = 71-indent. + + declare -i indent=12 + while [[ $indent -lt $flaglen ]]; do + indent+=8 + done + local fmt2 fmt1 + fmt2=" %-${indent}s %s\n" + printf -v fmt1 " %-${indent}s %%s\n" '' + + while [[ $# -gt 0 ]]; do + if [[ $1 == *: ]]; then + printf -- ' %s\n' "$(_ "$1")" + shift + else + [[ $# -gt 1 ]] || panic + local flag=$1 + local desc="$(_ "$(whitespace_collapse <<<"$2")")" + shift 2 + + local lines + IFS=$'\n' lines=($(fmt -u -w $((71-indent)) <<<"$desc")) + printf -- "$fmt2" "$flag" "${lines[0]}" + [[ ${#lines[@]} -lt 2 ]] || printf -- "$fmt1" "${lines[@]:1}" + fi + done +} + +# Usage: term_title MESG [ARGS...] +# +# Sets the terminal title. +term_title() { + [[ $# -ge 1 ]] || panic + local fmt='' + case "$TERM" in + screen|tmux) fmt='\ek%s\e\\';; + xterm*|rxvt*) fmt='\e]0;%s\a';; + esac + printf "$fmt" "$(printf -- "$@")" +} + +# Usage: setup_traps [handler] +# +# Sets up traps on TERM, HUP, QUIT and INT signals, as well as the ERR +# event, similar to makepkg. +# +# If `handler` is specified, instead of using the default handler +# (which is good for most purposes), it will call the command handler +# with the arguments: +# +# ${handler} SIGNAL_NAME MESSAGE_FMT [MESSAGE_PARAMS...] +# +# where MESSAGE_* are printf-like stuff. +# +# This function is based on code from pacman:makepkg +setup_traps() { + [[ $# -le 1 ]] || panic + if [[ $# == 1 ]]; then + eval "_libremessages_trap_exit() { $1 \"\$@\"; }" + else + _libremessages_trap_exit() { + local signal=$1; shift + echo + error "$@" + trap -- "$signal" + kill "-$signal" "$$" + } + fi + set -E + for signal in TERM HUP QUIT; do + trap "_libremessages_trap_exit $signal '%s signal caught. Exiting...' $signal" $signal + done + trap '_libremessages_trap_exit INT "Aborted by user! Exiting..."' INT + trap '_libremessages_trap_exit USR1 "An unknown error has occurred. Exiting..."' ERR +} diff --git a/src/lib/messages.sh.3.ronn b/src/lib/messages.sh.3.ronn new file mode 120000 index 0000000..391ecbd --- /dev/null +++ b/src/lib/messages.sh.3.ronn @@ -0,0 +1 @@ +libremessages.1.ronn
\ No newline at end of file diff --git a/src/librefetch/.gitignore b/src/librefetch/.gitignore new file mode 100644 index 0000000..d34a2fc --- /dev/null +++ b/src/librefetch/.gitignore @@ -0,0 +1,2 @@ +librefetch-install +librefetch-makepkg.conf diff --git a/src/librefetch/Makefile b/src/librefetch/Makefile new file mode 100644 index 0000000..26ee2ee --- /dev/null +++ b/src/librefetch/Makefile @@ -0,0 +1,13 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +libretools-bins = librefetch librefetch-install +libretools-confs += librefetch-makepkg.conf +libretools-libexecs += $(filter librefetchdir/%,$(detect-exec)) +libretools-libs += $(filter-out $(libretools-libexecs),$(filter librefetchdir/%,$(detect-all))) +pots = $(libretools-bins) + +$(outdir)/librefetch-install: $(var)pkgconfdir +$(outdir)/librefetch-makepkg.conf: $(var)bindir + +include $(topsrcdir)/automake.tail.mk diff --git a/src/librefetch/librefetch b/src/librefetch/librefetch new file mode 100755 index 0000000..2b8af61 --- /dev/null +++ b/src/librefetch/librefetch @@ -0,0 +1,400 @@ +#!/usr/bin/env bash +# librefetch +# +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For just the create_signature() function: +# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2005 Aurelien Foret <orelien@chez.com> +# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +# create_signature() is taken from pacman:makepkg, which is GPLv2+, +# so we take the '+' to combine it with our GPLv3+. + +. "$(librelib conf)" +. "$(librelib messages)" +setup_traps + +tmpfiles=() +tmpdirs=() +trap 'rm -f -- "${tmpfiles[@]}"; rm -rf -- "${tmpdirs[@]}"' EXIT + +cmd=${0##*/} +usage() { + print "Usage: %s [OPTIONS] SOURCE_URL [OUTPUT_FILE]" "$cmd" + print "Usage: %s -[g|S|M|h]" "$cmd" + print "Downloads or creates a liberated source tarball." + echo + prose "The default mode is to create OUTPUT_FILE, first by trying + download mode, then create mode." + echo + prose "If OUTPUT_FILE isn't specified, it defaults to the non-directory + part of SOURCE_URL, in the current directory." + echo + prose "Unless '-C' is specified, if SOURCE_URL does not begin with a + configured mirror, create mode is inhibited." + echo + prose "In download mode, it simply tries to download SOURCE_URL. At the + beginning of a URL, 'libre://' expands to the first configured + mirror." + echo + prose "In create mode, it either looks at a build script and uses that + to create the source tarball, or it uses GPG to create a + signature (if OUTPUT_FILE ends with \`.sig\` or \`.sig.part\`). + If it is using GPG to create a signature, but the file that it is + trying to sign doesn't exist yet, it recurses on itself to first + create that file. SOURCE_URL is ignored, except that it is used + to set the default value of OUTPUT_FILE, and that it may be used + when recursing." + echo + prose "The default build script is 'PKGBUILD', or 'SRCBUILD' if it + exists." + echo + prose "Other options, if they are valid \`makepkg\` options, are passed + straight to makepkg." + echo + print "Example usage:" + print ' $ %s https://repo.parabola.nu/other/mypackage/mypackage-1.0.tar.gz' "$cmd" + echo + print "Options:" + flag 'Settings:' \ + "-C" "Force create mode (don't download)" \ + "-D" "Force download mode (don't create)" \ + "-p <$(_ FILE)>" "Use an alternate build script (instead of + 'PKGBUILD'). If an SRCBUILD exists in the same + directory, it is used instead" \ + 'Alternate modes:' \ + "-g, --geninteg" "Generate integrity checks for source files" \ + "-S, --srcbuild" "Print the effective build script (SRCBUILD)" \ + "-M, --makepkg" "Generate and print the location of the + effective makepkg script" \ + "-h, --help" "Show this message" +} + +main() { + BUILDFILE="$(realpath -Lm PKGBUILD)" + makepkg_opts=() + extra_opts=() + mode=download-create + if ! parse_options "$@"; then + usage >&2 + exit 1 + fi + + doit +} + +doit() { + # Mode: help ########################################################### + + if [[ $mode =~ help ]]; then + usage + exit 0 + fi + + ######################################################################## + + makepkg="$(modified_makepkg)" + + # Mode: makepkg ######################################################## + + if [[ $mode =~ makepkg ]]; then + printf '%s\n' "$makepkg" + exit 0 + else + tmpdirs+=("${makepkg%/*}") + fi + + ######################################################################## + + local BUILDFILEDIR="${BUILDFILE%/*}" + if [[ -f "${BUILDFILEDIR}/SRCBUILD" ]]; then + BUILDFILE="${BUILDFILEDIR}/SRCBUILD" + fi + if [[ ! -f "$BUILDFILE" ]]; then + error "%s does not exist." "$BUILDFILE" + exit 1 + fi + case "$BUILDFILE" in + */SRCBUILD) srcbuild="$(modified_srcbuild "$BUILDFILE")";; + *) srcbuild="$(modified_pkgbuild "$BUILDFILE")";; + esac + tmpfiles+=("$srcbuild") + + # Mode: checksums ###################################################### + + if [[ $mode =~ checksums ]]; then + "$makepkg" "${makepkg_opts[@]}" -g -p "$srcbuild" | + case ${BUILDFILE##*/} in + PKGBUILD) sed -e 's/^[a-z]/mk&/' -e 's/^\s/ &/';; + SRCBUILD) cat;; + esac + exit 0 + fi + + # Mode: srcbuild ####################################################### + + if [[ $mode =~ srcbuild ]]; then + cat "$srcbuild" + exit 0 + fi + + ######################################################################## + + local src="${extra_opts[0]}" + local dst="${extra_opts[1]:-${src##*/}}" + + # Don't canonicalize $src unless mode =~ download, and we've validated + # that $MIRRORS is configured. + + # Canonicalize $dst + dst="$(realpath -Lm -- "$dst")" + + # Mode: download ####################################################### + + if [[ $mode =~ download ]]; then + load_files librefetch + check_vars librefetch MIRRORS DOWNLOADER || exit 1 + + # Canonicalize $src + if [[ "$src" == libre://* ]]; then + src="${MIRRORS[0]}/${src#libre://}" + fi + + # check to see if $src is a candidate for create mode + local inmirror=false; + local mirror + for mirror in "${MIRRORS[@]}"; do + if [[ "$src" == "$mirror"* ]]; then + inmirror=true + break + fi + done + if ! $inmirror; then + # inhibit create + mode=download + fi + + local dlcmd="${DOWNLOADER}" + [[ $dlcmd = *%u* ]] || dlcmd="$dlcmd %u" + dlcmd="${dlcmd//\%o/\"\$dst\"}" + dlcmd="${dlcmd//\%u/\"\$src\"}" + + { eval "$dlcmd"; } >&2 && exit 0 + fi + + # Mode: create ######################################################### + + if [[ $mode =~ create ]]; then + local base_dst=${dst%.part} + local suffix=${dst#"$base_dst"} + + if [[ $base_dst == *.sig ]]; then + if ! [[ -e $base_dst ]]; then + extra_opts=("${src%.sig}" "${base_dst%.sig}") + doit || exit $? + fi + create_signature "${base_dst%.sig}" || exit $? + if [[ -n $suffix ]]; then + mv -f "$base_dst" "$dst" + fi + else + export PKGEXT=${base_dst##*/} + export PKGDEST=${dst%/*} + export pkg_file=$dst + + cd "$BUILDFILEDIR" + "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >&2 || exit $? + fi + fi +} + +# sets the variables BUILDFILE, makepkg_opts, extra_opts, mode +parse_options() { + declare -i ret=0 + local {shrt,long}{1,2} + + # makepkg options + local makepkg_orig="$(which makepkg)" + shrt1=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +-(.)(,| [^<]).*/\1/p')) + shrt2=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +-(.) <.*/\1/p')) + long1=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn -e 's/^ +(-., )?--(\S*) [^<].*/\2/p')) + long2=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +--(\S*) <.*/\1/p')) + + # librefetch options + shrt1+=(C D g S M h) + shrt2+=(p) + long1+=(geninteg srcbuild makepkg help) + long2+=() + + # Feed the options through getopt (sanitize them) + local shrt="$({ printf '%s\0' "${shrt1[@]}"; printf '%s:\0' "${shrt2[@]}"; } | sort -zu | xargs -0 printf '%s')" + local long="$({ printf '%s\0' "${long1[@]}"; printf '%s:\0' "${long2[@]}"; } | sort -zu | xargs -0 printf '%s,')" + local args="$(getopt -n "$cmd" -o "$shrt" -l "${long%,}" -- "$@")" + ret=$? + eval set -- "$args" + unset shrt long args + + # Parse the options. + local opt optarg have_optarg + while [[ $# -gt 0 ]]; do + opt=$1; shift + have_optarg=false + + if { [[ $opt == --?* ]] && in_array "${opt#--}" "${long2[@]}"; } \ + || { [[ $opt == -? ]] && in_array "${opt#-}" "${shrt2[@]}"; } + then + optarg=$1; shift + have_optarg=true + fi + + case "$opt" in + -C) mode=create;; + -D) mode=download;; + -g|--geninteg) mode=checksums;; + -S|--srcbuild) mode=srcbuild;; + -M|--makepkg) mode=makepkg;; + -p) BUILDFILE="$(realpath -Lm -- "$optarg")";; + -h|--help) mode=help;; + --) break;; + *) + makepkg_opts+=("$opt") + if $have_optarg; then makepkg_opts+=("$optarg"); fi + ;; + esac + done + extra_opts+=("$@") + + # check the number of extra_opts + case "$mode" in + help) # don't worry about it + :;; + checksums|srcbuild|makepkg) # don't take any extra arguments + if [[ ${#extra_opts[@]} != 0 ]]; then + print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >&2 + ret=1 + fi + ;; + *download*|*create*) # take 1 or 2 extra arguments + if [[ ${#extra_opts[@]} != 1 ]] && [[ ${#extra_opts[@]} != 2 ]]; then + print "%s: %d non-flag arguments found, expected 1 or 2: %s" "$cmd" ${#extra_opts[@]} >&2 + ret=1 + fi + ;; + esac + + return $ret +} + +# Modify makepkg ############################################################### + +modified_makepkg() { + local dir="$(mktemp --tmpdir --directory "${cmd}.XXXXXXXXXXX.makepkg")" + make -s -f "$(librelib librefetchdir/Makefile)" new="$dir" + realpath -es "$dir/makepkg" +} + +# Modify PKGBUILD ############################################################## + +# a string to be appended +pkgbuild_append=' +# do not do split packages +if [[ ${#pkgname[@]} -gt 1 ]]; then + if [[ -n $pkgbase ]]; then + pkgname=("$pkgbase") + else + pkgname=("$pkgname") + fi +fi + +# copy source variables +source=("${mksource[@]}") ; unset "source_${CARCH}" +noextract=("${mknoextract[@]}") + +declare algo +for algo in "${known_hash_algos[@]}"; do + eval "${algo}sums=(\"\${mk${algo}sums[@]}\")" + unset "${algo}sums_${CARCH}" +done + +depends=() ; unset "depends_${CARCH}" +checkdepends=() ; unset "checkdepends_${CARCH}" +makedepends=("${mkdepends[@]}") ; unset "makedepends_${CARCH}" + +#### +options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx !optipng !debug) +PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) + +#### +if ! declare -f mksource >/dev/null; then + mksource() { :; } +fi +prepare() { :; } +build() { mksource; } +check() { :; } +package() { cp -a "$srcdir"/*/ "$pkgdir/"; } +' + +modified_pkgbuild() { + local pkgbuild=$1 + local srcbuild="$(mktemp "${pkgbuild%/*}/${cmd}.XXXXXXXXXXX.PKGBUILD.to.SRCBUILD")" + printf '%s' "$pkgbuild_append" | cat "$pkgbuild" - > "$srcbuild" + printf '%s\n' "$srcbuild" +} + + +# Modify SRCBUILD ############################################################## + +modified_srcbuild() { + local orig=$1 + local srcbuild="$(mktemp "${orig%/*}/${cmd}.XXXXXXXXXXX.SRCBUILD.to.SRCBUILD")" + sed -e '/PKGDEST=/d' -e '/PKGEXT=/d' < "$orig" > "$new" + printf '%s\n' "$new" +} + +################################################################################ + +# This function is taken almost verbatim from makepkg +create_signature() { + local ret=0 + local filename="$1" + msg "Signing package..." + + local SIGNWITHKEY=() + if [[ -n $GPGKEY ]]; then + SIGNWITHKEY=(-u "${GPGKEY}") + fi + + gpg --detach-sign --use-agent "${SIGNWITHKEY[@]}" --no-armor "$filename" &>/dev/null || ret=$? + + + if (( ! ret )); then + msg2 "Created signature file %s." "$filename.sig" + else + error "Failed to sign package file." + return $ret + fi +} + +main "$@" diff --git a/src/librefetch/librefetch-install.in b/src/librefetch/librefetch-install.in new file mode 100644 index 0000000..34815a7 --- /dev/null +++ b/src/librefetch/librefetch-install.in @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# lirefetch-install: (un)install librefetch to /etc/makepkg.conf +# +# Copyright (C) 2013-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +set -ueE +. "$(librelib messages)" + +# This line should be installed +new_code='. @pkgconfdir@/librefetch-makepkg.conf # This line was added by librefetch-install' + +# These lines were installed by previous versions of this script +old_code=( + '# The following line is added by the libretools post_install script' + '[[ ! -x /usr/bin/librefetch ]] || DLAGENTS+=("libre::/usr/bin/librefetch -p \"\$BUILDFILE\" %u %o")' + '[[ ! -x /usr/bin/librefetch ]] || DLAGENTS+=({https,libre}"::/usr/bin/librefetch -p \"\$BUILDFILE\" -- %u %o")' + 'DLAGENTS+=({https,libre}"::/usr/bin/librefetch -p $(printf "%q" "$BUILDFILE") -- %u %o")' + 'DLAGENTS+=({https,libre}'\''::/usr/bin/librefetch -p "$BUILDFILE" -- %u %o'\'')' +) + +# has_line $file $line +has_line() { + local file=$1 + local line=$2 + grep -Fxq -- "$line" "$file" +} +# del_line $file $line +del_line() { + local file=$1 + local line=$2 + local lineno=($(grep -Fxn -- "$line" "$file" | cut -d: -f1)) + if [[ "${#lineno[@]}" -gt 0 ]]; then + sed -i "$(printf '%dd;' "${lineno[@]}")" "$file" + fi +} + +# post_install $file +post_install() { + local file=$1 + if grep -q 'librefetch' "$file"; then + msg2 "%s: librefetch is already in %q" "${0##*/}" "$(realpath -s "$file")" + local line del=false + for line in "${old_code[@]}"; do + if has_line "$file" "$line"; then + msg2 "%s: ... but it's an old version" "${0##*/}" + pre_remove "$file" + post_install "$file" + return $? + fi + done + else + msg2 "%s: adding librefetch to %q" "${0##*/}" "$(realpath -s "$file")" + printf '%s\n' "$new_code" >> "$file" + fi +} + +# pre_remove $file +pre_remove() { + local file=$1 + msg2 "%s: removing librefetch from %q" "${0##*/}" "$(realpath -s "$file")" + + # Check for the old stuff + sed -ri 's/^#(.*) # commented out by the libretools post_install script/\1/' "$file" + local line + for line in "${old_code[@]}"; do + del_line "$file" "$line" + done + + # Check for the current stuff + del_line "$file" "$new_code" +} + +usage() { + print "Usage: %s [install|remove] MAKEPKG_CONF_FILE" "${0##*/}" + print "Adds or removes librefetch to/from makepkg.conf:DLAGENTS" +} + +main() { + if [[ $# != 2 ]]; then + usage >&2 + return 1 + fi + local file=$2 + if [[ ! -f "$file" ]]; then + msg2 "%s: does not exist: %q" "${0##*/}" "$(realpath -s "$file")" + fi + if [[ ! -w "$file" ]]; then + msg2 "%s: cannot write to file: %q" "${0##*/}" "$(realpath -s "$file")" + fi + case "$1" in + install) post_install "$file";; + remove) pre_remove "$file";; + *) usage >&2; return 1;; + esac +} + +main "$@" diff --git a/src/librefetch/librefetch-makepkg.conf.in b/src/librefetch/librefetch-makepkg.conf.in new file mode 100644 index 0000000..723bc15 --- /dev/null +++ b/src/librefetch/librefetch-makepkg.conf.in @@ -0,0 +1,16 @@ +#!/hint/bash + +# This file should be 'source'd by makepkg.conf to enable librefetch +edit_dlagents() { + local dlagent="@bindir@/librefetch -p $(printf %q "$(realpath -Lm "${BUILDFILE:-${BUILDSCRIPT:-PKGBUILD}}")") -- %u %o" + + local i + for i in "${!DLAGENTS[@]}"; do + if [[ "${DLAGENTS[$i]}" = https::* ]]; then + DLAGENTS[$i]="https::$dlagent" + fi + done + DLAGENTS+=("libre::$dlagent") +} +edit_dlagents +unset -f edit_dlagents diff --git a/src/librefetch/librefetch.8.ronn b/src/librefetch/librefetch.8.ronn new file mode 100644 index 0000000..cf009de --- /dev/null +++ b/src/librefetch/librefetch.8.ronn @@ -0,0 +1,214 @@ +librefetch(8) -- downloads or creates a liberated source tarball +================================================================ + +## SYNOPSIS + +`librefetch` [<OPTIONS>] <SOURCE-URL> [<OUTPUT-FILE>]<br> +`librefetch` `-`[`g`|`S`|`M`|`h`] + +## DESCRIPTION + +`librefetch` is a program to streamline creation of custom source +tarballs for `PKGBUILD(5)` files. + +If a URL mentioned in the <source> array in a `PKGBUILD` is in a +location that Parabola uploads "custom" source tarballs to (or +configured locations), and no file is at that URL, librefetch will +automatically create it for you. + +This works because a post-install script for the package configures +`librefetch` as the download agent for `https://` URLs in +`makepkg.conf`; allowing it to jump in and create a file if need be. +Because of this, it is almost never necessary to call `librefetch` +manually. + +The post-install script also configures `librefetch` as the download +agent for `libre://` URLs, for compatibility with `PKGBUILD` files +that used a previous version of librefetch. + +There are 5 modes: + + * `download`: Download the tarball from the configured mirror. + * `create`: Create the tarball from a `PKGBUILD`/`SRCBUILD`. + * `checksums`: Generate integrity checks for source files. + * `srcbuild`: Print the effective build script. + * `makepkg`: Generate and print the location of the effective makepkg + script. + * `help`: Print `librefetch` usage information. + +The normal mode of operation is `download` mode. If `download` mode +fails, it may choose to try `create` mode. + +## OPTIONS + + * `-C`: Force `create` mode (don't download) + * `-D`: Force `download` mode (don't create) + * `-p` <file>: Use an alternate build script for `create` mode + (instead of `PKGBUILD`). If an `SRCBUILD` file exists in the same + directory, it is used instead. + * `-g` | `--geninteg`: Use `checksums` mode: Generate integrity + checks for source files. + * `-S` | `--srcbuild`: Use `srcbuild` mode: print the effective build + script. + * `-M` | `--makepkg`: Use `makepkg` mode: generate and print the + location of the effective makepkg script. + * `-h` | `--help`: Use `help` mode: Show usage information. + +Other options, if they are documented in `makepkg -h`, are passed to +the modified copy of makepkg created during `create` mode. + +## DOWNLOAD MODE + +If <SOURCE-URL> begins with the string `libre://`, it is replaced with +the first value in <MIRRORS>, as configured in `librefetch.conf(5)`; +this is for compatibility with `PKGBUILD` files that used a previous +version of librefetch. + +It uses <DOWNLOADER>, as configured in `librefetch.conf` to attempt to +download the source tarball from that URL. If that fails, and +following conditions are met, it proceeds to `create` mode: + + * The `-D` flag has not been specified to inhibit `create` mode. + * The <SOURCE-URL> begins with one of the values in <MIRRORS>. + +The latter requirement allows librefetch to be used as a generic +HTTP(S) download agent, that can automatically create files from +whitelisted locations. + +## CREATE MODE + +The principle of `create` mode is that a special `PKGBUILD(5)` (called +`SRCBUILD(5)`) installs source files to <$pkgdir>, and the resulting +"package" is then used as a source tarball. The `SRCBUILD` exists in +the same directory as the `PKGBUILD`. It can be created manually, or +generated on-the-fly from the `PKGBUILD`. Extra steps are taken to +ensure that as long as the same directory contents go in, an identical +tarball will come out--the checksum of the file should not change +based on when it is built or who builds it. + +The `SRCBUILD` is either created, or sanitized if it already exists. +If the output filename does not end with `.sig` or `.sig.part`, then +the `SRCBUILD` is fed to a modified version of `makepkg(8)`. If the +output filename does end with `.sig` or `.sig.part`, then it uses GPG +to create a signature. If the file it is trying to sign does not +exist yet, librefetch recurses on itself to create it. + +The reason `makepkg` must be modified is that we need the resulting +tarball to be deterministic (as well as not containing package +metadata). + +When this documentation speaks of a file being modified, it is a +temporary copy of the file that is modified, your original file will +remain intact. + +## SRCBUILD GENERATION + +As explained in the `CREATE MODE` section, in `create` mode, this +program generates an `SRCBUILD` file. For debugging purposes, this +file can be printed instead of executed with `srcbuild` mode. + +### PRE-EXISTING SRCBUILD + +The use of `SRCBUILD` files pre-dates `librefetch`. By convention, +they set <PKGDEST> and <PKGEXT> in `package()` in order to modify the +behavior of `makepkg`. Because a modified version of `makepkg` is +used, this interferes with the correct behavior. To compensate for +this, lines containing "`PKGDEST=`" or "`PKGEXT=`" are deleted from +the `SRCBUILD`. + +The general idea is that `build()` makes any modifications to the +source, then `package()` copies it from <$srcdir> to <$pkgdir>. + +### SRCBUILD FROM PKGBUILD + +Possibly more elegant than having a separate `SRCBUILD` file is having +an `mksource()` function in the main `PKGBUILD`. This results in less +boilerplate and fewer files to edit. + +Note that this only happens if a file named `SRCBUILD` doesn't already +exist; when migrating a package from a manually created `SRCBUILD` to +this method, the `SRCBUILD` must be deleted (or renamed) for this to +work. + +The dynamically created `SRCBUILD` is created by copying `PKGBUILD` to +a temporary file, then re-setting variables and re-defining functions. +Following is a table of the translations. + + Variables + source = mksource + noextract = mknoextract + *sums = mk*sums (md5, sha1, sha224, sha256, sha384, sha512) + depends = <empty> + checkdepends = <empty> + makedepends = mkdepends + *_$CARCH = <unset> + Functions + prepare() { :; } + build() { mksource; } + check() { :; } + package() { cp -a "$srcdir"/*/ "$pkgdir/"; } + +The `mksource()` function does not need to be defined. If it isn't +defined, then no transformations will be made to the source between it +being extracted to <$srcdir> and copied to <$pkgdir>. + +In summary: + + * Set <mksource=()> and <mkmd5sums=()> to act as <source=()> and + <md5sums=()>, respectively. + * Declare a `mksource()` function to make modifications to the + source, if necessary. + +Other changes: + + * <pkgname> is set to <pkgbase>, or the first element of the + <pkgname> array (the effect is that split packaging is turned + off). + * <options=()> is set have `makepkg` avoid making changes to + <$pkgdir>. The exact change is: + + options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx) + + * <PURGE_TARGETS=()> has VCS directories added to it: + + PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) + +### MAKEPKG MODIFICATIONS + +The following modifications are made to makepkg: + + * Allow us to manipulate the output file (<$pkg_file>) + * Do not include metadata in the output file (<${comp_files[@]}>) + * Force 'ustar' tar format, don't allow it to upgrade to 'pax' to + store extended file attributes. + * Don't symlink the resulting file into the current directory. + * <PURGE_TARGETS> interprets an item as a directory if it ends with a + slash ("/"). + * Timestamps in <$pkgdir> are reset to the date specified in + <SOURCE_DATE_EPOCH>[1], or "1990-01-01 0:0:0 +0" if it's not set, so + that the resulting tarball will be the same, regardless of when it + was created. + * Sort the files included in the tarball; normally the order of files + in a tarball is essentially random (even if it tends to be the same + when re-created on the same machine). + * append `-libre` to <$srcdir> + * append `-libre` to <$pkgbasedir> (which becomes <$pkgdir>) + * Don't check if the package has already been built. + +For debugging purposes, this modified makepkg can be printed instead +of executed with `makepkg` mode. Before it is run in create mode, +`PKGEXT`, `PKGDEST`, and `pkg_file` are set as environment variables. + +## CONFIGURATION + +See `librefetch.conf(5)` for details on configuring librefetch using +the `librefetch.conf` file. + +## SEE ALSO + +librefetch.conf(5), makepkg(8), PKGBUILD(5), SRCBUILD(5) + +## NOTES + + 1. <SOURCE_DATE_EPOCH> specification for build systems + https://reproducible-builds.org/specs/source-date-epoch/ diff --git a/src/librefetch/librefetch.conf b/src/librefetch/librefetch.conf new file mode 100644 index 0000000..7251ff3 --- /dev/null +++ b/src/librefetch/librefetch.conf @@ -0,0 +1,7 @@ +MIRRORS=( + 'https://repo.parabola.nu/sources/' + 'https://repo.parabola.nu/other/' + 'https://repo.parabolagnulinux.org/sources/' + 'https://repo.parabolagnulinux.org/other/' +) +DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' diff --git a/src/librefetch/librefetch.conf.5.ronn b/src/librefetch/librefetch.conf.5.ronn new file mode 100644 index 0000000..29df4d2 --- /dev/null +++ b/src/librefetch/librefetch.conf.5.ronn @@ -0,0 +1,42 @@ +librefetch.conf(5) -- librefetch configuration file +=================================================== + +## SYNOPSIS + +`/etc/libretools.d/librefetch.conf`, `~/.config/libretools/librefetch.conf` + +## DESCRIPTION + +Configuration for librefetch is stored in `librefetch.conf`. The +several places it looks for the file are: + + * `/etc/libretools.d/librefetch.conf` + * `$XDG_CONFIG_HOME/libretools/librefetch.conf` + +The later files take precedence over earlier files, but earlier files +are loaded, so that later files only need to set the values they want +to override. + +If `$XDG_CONFIG_HOME` is not set, a default value is set: + + * if `$SUDO_USER` is set: `$(eval echo ~$SUDO_USER)/.config` + * else: `$HOME/.config` + +## OPTIONS + + * `MIRRORS=( ... )`: + A list of locations that generated source tarballs may be located + at. If a URL begins with `libre://`, then `libre://` is replaced + with the first location listed here. + * `DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u'`: + The HTTP client to use when downloading pre-built source tarballs + in download mode. The format and semantics are similar to + `DLAGENTS` in `makepkg.conf`(5). If present, `%u` will be replaced + with the download URL (correctly quoted), otherwise the download + URL will be appended to the end of the command. If present, `%o` + will be replaced with the local filename that it should be + downloaded to. + +## SEE ALSO + +librefetch(8), makepkg.conf(5) diff --git a/src/librefetch/librefetchdir/Makefile b/src/librefetch/librefetchdir/Makefile new file mode 100644 index 0000000..58116bb --- /dev/null +++ b/src/librefetch/librefetchdir/Makefile @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# librefetchdir/Makefile +# +# Copyright (C) 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +# new = +librefetchdir := $(dir $(lastword $(MAKEFILE_LIST))) + +old_makepkg := $(shell which makepkg) +old_library := $(shell $(shell grep LIBRARY= $(old_makepkg)); echo $$LIBRARY) + +new_makepkg = $(new)/makepkg +new_library = $(new)/libmakepkg + +targets += $(new_makepkg) +targets += $(patsubst $(old_library)/%,$(new_library)/%,$(shell find $(old_library) -type f)) +targets += $(new_library)/tidy/~source_date_epoch.sh + +all: $(targets) +.PHONY: all + +.SECONDARY: +.DELETE_ON_ERROR: + +$(new_makepkg): $(old_makepkg) $(librefetchdir)/makepkg.gen + <$^ | install -Dm755 /dev/stdin $@ + +$(new_library)/source.sh: \ +$(new_library)/%: $(old_library)/% $(librefetchdir)/libmakepkg/%.gen + <$^ | install -Dm755 /dev/stdin $@ + +$(new_library)/tidy/purge.sh $(new_library)/tidy/~source_date_epoch.sh: \ +$(new_library)/%: $(librefetchdir)/libmakepkg/% + mkdir -p $(@D) + ln -sfT $(abspath $< $@) + +$(new_library)/%: $(old_library)/% + mkdir -p $(@D) + ln -sfT $(abspath $< $@) diff --git a/src/librefetch/librefetchdir/libmakepkg/source.sh.gen b/src/librefetch/librefetchdir/libmakepkg/source.sh.gen new file mode 100755 index 0000000..269f9fd --- /dev/null +++ b/src/librefetch/librefetchdir/libmakepkg/source.sh.gen @@ -0,0 +1,25 @@ +#!/usr/bin/sed -rf +# librefetchdir/makepkg.gen +# +# Copyright (C) 2013, 2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +/download_sources\(\) \{/ { + arm -rf "$srcdir"\nmkdir "$srcdir" +} diff --git a/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh b/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh new file mode 100755 index 0000000..acb0030 --- /dev/null +++ b/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh @@ -0,0 +1,58 @@ +#!/usr/bin/bash +# +# purge.sh - Remove unwanted files from the package +# +# Copyright (c) 2008-2016 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (c) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This file is part of LibreFetch +# +# This program 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 2 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/>. +# + +[[ -n "$LIBMAKEPKG_TIDY_PURGE_SH" ]] && return +LIBMAKEPKG_TIDY_PURGE_SH=1 + +LIBRARY=${LIBRARY:-'/usr/share/makepkg'} + +source "$LIBRARY/util/message.sh" +source "$LIBRARY/util/option.sh" + + +packaging_options+=('purge') +tidy_remove+=('tidy_purge') + +tidy_purge() { + if check_option "purge" "y" && [[ -n ${PURGE_TARGETS[*]} ]]; then + msg2 "$(gettext "Purging unwanted files...")" + local pt + for pt in "${PURGE_TARGETS[@]}"; do + if [[ ${pt} = "${pt%/}" ]]; then + if [[ ${pt} = "${pt//\/}" ]]; then + find . ! -type d -name "${pt}" -exec rm -f -- '{}' + + else + rm -f "${pt}" + fi + else + if [[ ${pt%/} = "${pt//\/}" ]]; then + find . -type d -name "${pt%/}" -exec rm -rf -- '{}' + + else + rm -rf "${pt}" + fi + fi + done + fi +} diff --git a/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh b/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh new file mode 100755 index 0000000..9141d67 --- /dev/null +++ b/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh @@ -0,0 +1,38 @@ +#!/usr/bin/bash +# +# librefetchdir/libmakepkg/tidy/~source_date_epoch.sh +# +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +# This filename starts with a ~ because it sorts after every other +# (printable) ASCII character, and we want this to run last. + +[[ -n "$LIBMAKEPKG_TIDY_SOURCE_DATE_EPOCH_SH" ]] && return +LIBMAKEPKG_TIDY_SOURCE_DATE_EPOCH_SH=1 + +tidy_modify+=('tidy_source_date_epoch') + +tidy_source_date_epoch() { + local date='1990-01-01 0:0:0 +0' + if [[ -n "$SOURCE_DATE_EPOCH" ]]; then + date="@$SOURCE_DATE_EPOCH" + fi + find . -exec touch --no-dereference --date="$date" -- {} + +} diff --git a/src/librefetch/librefetchdir/makepkg.gen b/src/librefetch/librefetchdir/makepkg.gen new file mode 100755 index 0000000..8928d91 --- /dev/null +++ b/src/librefetch/librefetchdir/makepkg.gen @@ -0,0 +1,42 @@ +#!/usr/bin/sed -rf +# librefetchdir/makepkg.gen +# +# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of LibreFetch. +# +# LibreFetch 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. +# +# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>. + +/LIBRARY=/iexport LIBRARY="${0%/*}/libmakepkg" + +/create_package\(\) \{/,/^\}$/ { + /pkg_file=/d # allow us to set pkg_file + s/"?\$\{comp_files\[@\]\}"?// # do not include .{PKGINFO,BUILDINGO,CHANGELOG,INSTALL,MTREE} + # This is long/gross. What it does: + # - pass --format=ustar to bsdtar, to inhibit it using the pax format + # - take the files that would be included in the tarball, and use + # find/sort/--files-from to order them for bsdtar + s/bsdtar(.*) - ([^|]*) \|/find \2 -print0 | LC_ALL=C sort --zero-terminated | bsdtar --null --files-from - --format=ustar --no-recursion \1 - |/ + s/create_signature .*/&; return $?/ # do not procede to create symlinks +} + +s|Making package:|Making source:| +s|Checking runtime dependencies\.\.\.|Checking source dependencies...| + /Checking buildtime dependencies\.\.\./d + +s|srcdir=.*|&-libre| +s|pkgdirbase=.*|&-libre| +s|check_build_status$|:| diff --git a/src/libretools.conf b/src/libretools.conf new file mode 100644 index 0000000..fb4593c --- /dev/null +++ b/src/libretools.conf @@ -0,0 +1,72 @@ +#!/hint/bash + +################################################################################ +# misc # +################################################################################ + +# The dir where you work on +WORKDIR="$LIBREHOME/packages" + +## Blacklist URL +BLACKLIST=https://projects.parabola.nu/blacklist.git/plain/blacklist.txt + +## Diff tool (vimdiff, gvimdiff, meld, etc) +## Used by `aur`, `diff-unfree` +DIFFPROG=$(which $([ -z "${DISPLAY:-}" ]||echo kdiff3 meld gvimdiff) vimdiff colordiff diff 2>/dev/null|sed 's/\s.*//;1q') + +## The architectures you'll be packaging for +## Used by `librestage`, `xbs-abslibre` +ARCHES=($(printf '%s\n' /usr/share/pacman/defaults/pacman.conf.*|sed 's|.*\.||')) + +## ABSLibre +# Used by xbs-abslibre +ABSLIBRERECV=git://projects.parabola.nu/abslibre/abslibre.git +ABSLIBRESEND=ssh://git@projects.parabola.nu/srv/git/abslibre/abslibre.git +ABSLIBREDEST="$WORKDIR/staging/abslibre" + +################################################################################ +# librerelease # +################################################################################ + +## Where to upload packages to +# '/staging/' is appended; this is for compatibility with previous versions. +REPODEST=repo@parabola.nu:staging/$LIBREUSER + +## These are run before and after uploading packages +HOOKPRERELEASE="ssh -fN ${REPODEST%%:*}" +HOOKPOSTRELEASE="sudo librechroot clean-repo" + +################################################################################ +# dagpkg # +################################################################################ + +# Note: Not being set is valid for any of the HOOK* settings. + +# Run a command before running FULLBUILDCMD +HOOKPREBUILD="" + +## Uncomment one of those or make one of your choice +# Normal +FULLBUILDCMD="sudo libremakepkg" +# Cross compiling +#FULLBUILDCMD="sudo libremakepkg -n cross-compile-chroot" +# Don't use a chroot +#FULLBUILDCMD="makepkg -sL --noconfirm" + +# Locally release the package or any other action after running FULLBUILDCMD +# successfully. When run, it is given a repository name as a single argument. +HOOKLOCALRELEASE="librestage" + +################################################################################ +# toru # +################################################################################ + +TORUPATH=/var/lib/libretools/toru + +## The repos you'll be packaging for +## Used by: `toru-path` +# Tip: As early repos take precedence on $REPOS loops, you can use this as +# inverted order of precedence. Put testing repos first so dagpkg will find new +# PKGBUILDs first, for instance. `toru-path` uses reverse order to enforce repo +# precedence on the path cache (the last path added replaces the rest) +REPOS=('core' 'libre' 'extra' 'community' 'libre-testing' 'social' 'sugar' 'pcr' 'java') diff --git a/src/pkgbuild-check-nonfree b/src/pkgbuild-check-nonfree new file mode 100755 index 0000000..67f07bc --- /dev/null +++ b/src/pkgbuild-check-nonfree @@ -0,0 +1,309 @@ +#!/usr/bin/env bash +# -*- tab-width: 4 ; sh-basic-offset: 4 -*- +# pkgbuild-check-nonfree + +# Copyright (C) 2011 Joseph Graham (Xylon) <joe@t67.eu> +# Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +# I appologize that this program got *huge*. +# It's not complicated, just long. + +. "$(librelib messages)" +. "$(librelib conf)" +. "$(librelib blacklist)" + +usage() { + print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" + print "Analyzes a PKGBUILD for freedom issues" + echo + prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.' + echo + print "Exit status (add them for combinations):" + print " 0: Everything OK, no freedom issues" + print " 1: Ran with error" + print "Warning-level freedom issues:" + print " 2: Uses unrecognized licenses, check them" + print " 4: Uses GPL-incompatible licenses" + print "Error-level freedom issues:" + print " 8: Uses known unacceptable licenses" + print " 16: Has nonfree dependencies" + print " 32: Is a known nonfree package" + echo + print "Options:" + flag '-c' 'Use the cached blacklist, do not try downloading' + flag '-f' 'Allow running as root user' + echo + flag '-q' 'Be quiet' + flag '-v' 'Be verbose' + echo + flag '-h' 'Show this message' +} +# Make sure these match pkgbuild-summarize-nonfree +declare -ri _E_OK=0 +declare -ri _E_ERROR=1 +declare -ri _E_LIC_UNKNOWN=2 +declare -ri _E_LIC_NOGPL=4 +declare -ri _E_LIC_NONFREE=8 +declare -ri _E_DEP_NONFREE=16 +declare -ri _E_PKG_NONFREE=32 + +main() { + # Parse flags + local cache=false + local asroot=false + local v=1 + while getopts 'cfqvh' arg; do + case "$arg" in + c) cache=true;; + f) asroot=true;; + q) v=0;; + v) v=2;; + h) usage; return $_E_OK;; + *) usage >&2; return $_E_ERROR;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -lt 1 ]]; then + pkgbuilds=("`pwd`/PKGBUILD") + else + pkgbuilds=("$@") + fi + + # Do a check to see if we are running as root + if [[ -w / ]] && ! $asroot; then + error "Run as normal user, or use the -f option to run as root." + return 1 + fi + + # Adjust the verbosity + if [[ $v == 0 ]]; then + error() { :; } + warning() { :; } + plain() { :; } + info() { :; } + elif [[ $v == 1 ]]; then + info() { :; } + elif [[ $v == 2 ]]; then + info() { plain "$@"; } + fi + + # Update the blacklist + $cache || blacklist-update || return $_E_ERROR + + # Do the work + declare -i ret=0 + local pkgbuild + for pkgbuild in "${pkgbuilds[@]}"; do + pkgbuild_check "$pkgbuild" || ret=$(($ret|$?)) + done + return $ret +} + +# Helper functions ############################################################# +# These should maybe be moved into lib/conf.sh + +# Usage: var="$(pkgbuild_get_pkg_str ${pkgname} ${varname})" +# Gets a package-level string for a split-package +pkgbuild_get_pkg_str() { + [[ $# == 2 ]] || panic 'malformed call to pkgbuild_get_pkg_str' + local pkg=$1 + local var=$2 + + local indirect=${!var} + eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p") + printf '%s' "${indirect}" +} +# Usage: eval $(pkgbuild_get_pkg_ary ${pkgname} ${varname} [$variable_name_to_set]) +# Gets a package-level array for a split-package +pkgbuild_get_pkg_ary() { + [[ $# == 2 ]] || [[ $# == 3 ]] || panic 'malformed call to pkgbuild_get_pkg_ary' + local pkg=$1 + local var=$2 + local out="${3:-$var}" + + local ary="${var}[@]" + local indirect=("${!ary}") + eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p") + declare -p indirect|sed "s/ indirect=/ ${out}=/" +} + +# Checker functions ############################################################ + +# Usage: check_lic "${licence}" +# Check a license name to see if it is OK +check_lic() { + [[ $# == 1 ]] || panic 'malformed call to check_license' + local license=$1 + + info 'Checking license: %s' "$license" + + if [[ -e "/usr/share/licenses/common/$license" ]]; then + return $_E_OK + else + case "${license#custom:}" in + WTFPL) + # accept as common, I think it should be in the licenses package + return $_E_OK;; + BSD1|BSD2|BSD3|MIT|X11) + # accept these as common; they can't be included in the + # 'licenses' package because some text must be customized + return $_E_OK;; + BSD4) + warning "The 4-clause BSD license is free but has practical problems." + return $_E_LIC_NOGPL;; + BSD) + warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses." + return $_E_LIC_UNKNOWN;; + JSON) + error "License '%s' is a known non-free license." "$license" + return $_E_LIC_NONFREE;; + *) + warning "License '%s' is not a common (recognized) license." "$license" + return $_E_LIC_UNKNOWN;; + esac + fi + panic 'code should never be reached' +} + +# Usage: check_dep "${dependency}" +# Checks for ${dependency} in the blacklist +check_dep() { + [[ $# == 1 ]] || panic 'malformed call to check_dep' + local pkg=$1 + + local line="$(blacklist-cat|blacklist-lookup "$pkg")" + local rep="$(blacklist-get-rep <<<"$line")" + if [[ -z $line ]]; then + # not mentioned in blacklist; free + info '%s: not blacklisted' "$pkg" + return $_E_OK + elif [[ -z $rep ]]; then + # non-free with no replacement + plain '%s: blacklisted' "$pkg" + return $_E_DEP_NONFREE + else + # non-free with free replacement + if [[ "$rep" == "$pkg" ]]; then + info '%s: repackaged with the same name' "$pkg" + else + info '%s: replaced by %s' "$pkg" "$rep" + fi + return $_E_OK + fi + panic 'code should never be reached' +} + +# Usage: check_pkg "${pkgname}" +# Checks for ${pkgname} in the blacklist +check_pkg() { + [[ $# == 1 ]] || panic 'malformed call to check_pkg' + check_dep "$@" + case $? in + $_E_OK) + return $_E_OK;; + $_E_DEP_NONFREE) + return $_E_PKG_NONFREE;; + *) + panic 'unexpected return code from check_dep';; + esac + panic 'code should never be reached' +} + +# Usage: pkgbuild_ckec $pkgbuild +# Check whether a PKGBUILD has any issues (using the above) +pkgbuild_check() ( + [[ $# == 1 ]] || panic 'malformed call to pkgbuild_check' + local pkgbuild=$1 + + load_PKGBUILD "$pkgbuild" + if [[ -z $pkgname ]]; then + return $_E_ERROR # not a PKGBUILD + fi + + declare -i ret=0 # the return status + local dep lic # iterators for us in `for` loops + local ck_deps ck_lics # lists of deps and licenses that have been checked + + if [[ ${#pkgname[@]} == 1 ]]; then + msg2 'Inspecting package pkgname=%q (%s)' "$pkgname" "$(get_full_version)" + else + msg2 'Inspecting split package pkgbase=%q (%s)' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" + fi + + # Check if this is blacklisted + check_pkg "${pkgbase:-${pkgname[0]}}" || ret=$(($ret|$?)) + # Check if dependencies are blacklisted + for dep in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" \ + "${optdepends[@]%%:*}" "${mkdepends[@]}" + do + check_dep "$dep" || ret=$(($ret|$?)) + ck_deps+=("$dep") + done + # Check the licenses + for lic in "${license[@]}"; do + check_lic "$lic" || ret=$(($ret|$?)) + ck_lics+=("$lic") + done + + if [[ ${#pkgname[@]} == 1 ]]; then + # Non-split package + # Make sure a license is set + if [[ ${#ck_lics[@]} == 0 ]]; then + error "The license array is empty" + ret=$(($ret|$_E_ERROR)) + fi + else + # Split package + # Check the individual split packages + local _pkgname _license _depends _optdepends + for _pkgname in "${pkgname[@]}"; do + msg2 'Inspecting split package pkgname=%q (%s)' "$_pkgname" "$(get_full_version "$_pkgname")" + eval $(pkgbuild_get_pkg_ary "$_pkgname" license _license) + eval $(pkgbuild_get_pkg_ary "$_pkgname" depends _depends) + eval $(pkgbuild_get_pkg_ary "$_pkgname" optdepends _optdepends) + + # Check if this is blacklisted + check_pkg "$_pkgname" || ret=$(($ret|$?)) + # Check if dependencies are blacklisted + for dep in "${_depends[@]}" "${_optdepends[@]%%:*}"; do + if ! in_array "$dep" "${ck_deps[@]}"; then + check_dep "$dep" || ret=$(($ret|$?)) + fi + done + # Check the licenses + for lic in "${_license[@]}"; do + if ! in_array "$lic" "${ck_lics[@]}"; then + check_lic "$lic" || ret=$(($ret|$?)) + fi + done + + if [[ ${#_license[@]} == 0 ]]; then + error "The license array is empty" + ret=$(($ret|$_E_ERROR)) + fi + done + fi + + return $ret +) + +main "$@" diff --git a/src/pkgbuild-summarize-nonfree b/src/pkgbuild-summarize-nonfree new file mode 100755 index 0000000..1e39e87 --- /dev/null +++ b/src/pkgbuild-summarize-nonfree @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This file is part of Parabola. +# +# Parabola 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. +# +# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>. + +. "$(librelib messages)" + +# Make sure these match pkgbuild-check-nonfree +declare -ri _E_OK=0 +declare -ri _E_ERROR=1 +declare -ri _E_LIC_UNKNOWN=2 +declare -ri _E_LIC_NOGPL=4 +declare -ri _E_LIC_NONFREE=8 +declare -ri _E_DEP_NONFREE=16 +declare -ri _E_PKG_NONFREE=32 + +usage() { + print "Usage: %s [OPTIONS] STATUS" "${0##*/}" + print "Summarizes a status code from pkgbuild-check-nonfree" + echo + prose 'It thresholds the issues it finds, only failing for error-level + issues, and ignoring warnings. Unless `-q` is specified, it also + prints a summary of the issues it found.' + echo + print 'Options:' + flag '-q' 'Be quiet' + flag '-h' 'Show this message' +} + +main() { + local quiet=false + while getopts 'qh' arg; do + case "$arg" in + q) quiet=true;; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -ne 1 ]]; then + usage >&2 + return 1 + fi + if ! [[ $1 =~ ^[0-9]+$ ]]; then + error 'STATUS must be an integer' + usage >&2 + return 1 + fi + + if $quiet; then + error() { :; } + warning() { :; } + fi + + parse $1; + return $? +} + +parse() { + [[ $# == 1 ]] || panic 'malformed call to parse' + declare -i s=$1; + + declare -i ret=0 + declare -i i + for i in 1 2 4 8 16 32; do + if [[ $(($s & $i)) -gt 0 ]]; then + case $i in + $_E_ERROR) + # could be anything, assume the worst + error "There was an error processing the PKGBUILD" + ret=1;; + $_E_LIC_UNKNOWN) + warning "This PKGBUILD has an unknown license";; + $_E_LIC_NOGPL) + warning "This PKGBUILD has a GPL-incompatible license";; + $_E_LIC_NONFREE) + error "This PKGBUILD has a known nonfree license" + ret=1;; + $_E_DEP_NONFREE) + error "This PKGBUILD depends on known nonfree packages" + ret=1;; + $_E_PKG_NONFREE) + error "This PKGBUILD is for a known nonfree package" + ret=1;; + esac + fi + done + return $ret +} + +main "$@" diff --git a/src/repo-diff b/src/repo-diff new file mode 100755 index 0000000..d11732b --- /dev/null +++ b/src/repo-diff @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Shows a diff between repo databases + +# Copyright (C) 2013 Nicolás Reynolds <fauno@parabola.nu> +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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/>. + +. libremessages + +usage() { + print "Usage: %s arch/core/i686 parabola/core/i686" "${0##*/}" + print "Compares two repo databases using distro/repo/architecture format." + echo + print 'Shortcuts:' + flag 'arch' 'expands to Arch Linux repo url' + flag 'parabola' 'expands to Parabola GNU/Linux-libre repo url' +} + +if test $# -eq 0; then + usage + exit 0 +fi + +b() { bsdtar ztf $1 | cut -d "/" -f1 | sort -u ; } +n() { echo "$1".db | tr "/" "-"; } + +# hopefully simple way to convert +# parabola/libre/i686 +# to +# http://repo.parabola.nu/libre/os/i686/libre.db +# add more distros here +g() { + echo "$1" | sed -e "s,^\([^/]\+\)/\([^/]\+\)/\([^/]\+\)$,\1/\2/os/\3/\2.db," \ + -e "s,^parabola/,http://repo.parabola.nu/," \ + -e "s,^arch\(linux\)\?/,http://mirrors.kernel.org/archlinux/," +} + +mkdir ${0##*/}.$$ +pushd ${0##*/}.$$ >/dev/null + +d="" +for i in $1 $2; do + n=$(n "$i") + + test -z "$n" && exit 1 + + wget -O "$n" -nv $(g "$i") + b "$n" >${n}.orig + + d+=" ${n}.orig" +done + +{ + printf "$(gettext "Difference between %s and %s")\n---\n" $1 $2 + which diffstat &>/dev/null && diff -auN "${d[@]}" | diffstat + diff -auN "${d[@]}" +} >../${n}.diff + +popd >/dev/null +rm -r ${0##*/}.$$ + +print "Difference save on %s" "${n}.diff" diff --git a/src/toru/Makefile b/src/toru/Makefile new file mode 100644 index 0000000..2903f4a --- /dev/null +++ b/src/toru/Makefile @@ -0,0 +1,4 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk + +include $(topsrcdir)/automake.tail.mk diff --git a/src/toru/toru-info b/src/toru/toru-info new file mode 100755 index 0000000..a7081cb --- /dev/null +++ b/src/toru/toru-info @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Prints info about a given pkgname + +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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/>. + +. libremessages +. "$(librelib conf)" + +for _pkg in "$@"; do + _pkgbuild="$(toru-where $_pkg)" + + if [ -f "$_pkgbuild/PKGBUILD" ]; then + if ! load_PKGBUILD "$_pkgbuild/PKGBUILD" 2>/dev/null; then + warning "Errors on %s" "$_pkg" + continue + fi + + deps=("${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}") + repo="$(basename -- "$(dirname -- "$_pkgbuild")")" + + msg "%s/%s %s-%s" "$repo" "$_pkg" "$pkgver" "$pkgrel" + msg2 '%s' "$pkgdesc" + msg2 '%s' "$url" + msg2 'Depends: %s' "${deps[*]}" + else + warning "%s doesn't exist" "$_pkg" + fi +done diff --git a/src/toru/toru-path b/src/toru/toru-path new file mode 100755 index 0000000..4600a5c --- /dev/null +++ b/src/toru/toru-path @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com> +# Copyright (C) 2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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/>. + +. libremessages +setup_traps + +# TODO: better option parsing +TORUPATH=${T:-${TORUPATH}} +VERBOSE=${V:-false} +FORCE=${F:-false} + +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools TORUPATH REPOS || exit 1 +load_files abs +check_vars abs ABSROOT || exit 1 + +if [ ! -w "$TORUPATH" ]; then + error "Toru's path isn't writable. Please check $TORUPATH" + exit 1 +fi + +lastsyncfile=${TORUPATH}/lastsync.paths +pathfile=${TORUPATH}/paths.tch + +if [ ! -e "${pathfile}" ]; then + tcamgr create "${pathfile}" +fi + +# TODO: ability to use flags to pass in other directories to fullrepos + +# This loops over ${REPOS[@]} backward. This is because early entries +# in REPOS have higher precidence, but the way this is implemented, +# the later entries have precedence, so we need to flip the order. +fullrepos=() +for (( i = ${#REPOS[@]}-1 ; i >= 0 ; i-- )); do + $VERBOSE && msg "Processing [%s]" "${REPOS[$i]}" + + # ABSROOT has trailing slash + if [ -d "${ABSROOT}${REPOS[$i]}" ]; then + fullrepos+=("${ABSROOT}${REPOS[$i]}") + fi +done + +# Find PKGBUILDs in ${fullrepos[@]} +find_args=("${fullrepos[@]}" -mindepth 2 -maxdepth 3 -type f -name PKGBUILD) +if ! $FORCE && [[ -e $lastsyncfile ]]; then + # if lastfilesync exists, only look at things that have + # changed since then (unless $FORCE is on) + find_args+=(-newer "${lastsyncfile}") +fi +IFS=$'\n' +pkgbuilds=($(find "${find_args[@]}")) + +# Add information from each of the PKGBUILDs to the toru cache. +msg "Updating path cache" +msg2 "%d PKGBUILDs to update" ${#pkgbuilds[@]} +for pkgbuild in "${pkgbuilds[@]}"; do + # plain "$_pkgbuild" + if ! load_PKGBUILD "${_pkgbuild}" >/dev/null 2>&1; then + error "%q contains errors, skipping" "${_pkgbuild}" + continue + fi + + fullpath="$(dirname -- "${_pkgbuild}")" + + for _pkg in "${pkgbase}" "${pkgname[@]}" "${provides[@]}"; do + $VERBOSE && msg2 '%s -> %s' "${_pkg}" "${fullpath}" + tcamgr put "${pathfile}" "${_pkg%%[<>=]*}" "${fullpath}" + done +done + +date +%s > "${lastsyncfile}" diff --git a/src/toru/toru-where b/src/toru/toru-where new file mode 100755 index 0000000..f6df15f --- /dev/null +++ b/src/toru/toru-where @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Locates a PKGBUILD dir on toru's path cache + +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> +# +# License: GNU GPLv3+ +# +# This program 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 Affero 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/>. + +. "$(librelib conf.sh)" +load_files libretools +check_vars libretools TORUPATH || exit 1 + +tcamgr get "${TORUPATH}/paths.tch" "$1" 2>/dev/null || echo "" diff --git a/src/workflows.md b/src/workflows.md new file mode 100644 index 0000000..03dca4f --- /dev/null +++ b/src/workflows.md @@ -0,0 +1,64 @@ +# Workflows + +Describe your packaging workflow here! + + +## fauno's way + +During packaging, I don't usually restart a build from scratch if I have to make +changes to the PKGBUILD. I use a lot of commenting out commands already ran, +`makepkg -R`, etc. When I used `libremakepkg` I ended up using a lot more +`librechroot` and working from inside the unconfigured chroot, because +`makechrootpkg` (the underlying technology for `libremakepkg`) tries to be too +smart. + +When I started writing `treepkg` I found that mounting what I need directly on +the chroot and working from inside it was much more comfortable and simple than +having a makepkg wrapper doing funny stuff (for instance, mangling +`makepkg.conf` and breaking everything.) + +This is how the chroot is configured: + +* Create the same user (with same uid) on the chroot that the one I use + regularly. + +* Give it password-less sudo on the chroot. + +* Bind mount `/home` to `/chroot/home`, where I have the abslibre-mips64el + clone. + +* Bind mount `/var/cache/pacman/pkg` to `/chroot/var/cache/pacman/pkg` + +* Put these on system's `fstab` so I don't have to do it everytime + +* Configure `makepkg.conf` to `PKGDEST=CacheDir` and `SRCDEST` to something on + my home. + +Workflow: + +* Enter the chroot with `systemd-nspawn -D/chroot` and `su - fauno`. + +* From another shell (I use tmux) edit the abslibre or search for updates with + `git log --no-merges --numstat`. + +* Pick a package and run `treepkg` from its dir on the chroot, or retake a build + with `treepkg /tmp/package-treepkg-xxxx`. + + > Note: `treepkg` has been deprecated in favor of `dagpkg`. + +What this allows: + +* Not having to worry about the state of the chroot. `chcleanup` removes and + adds packages in a smart way so shared dependencies stay and others move along + (think of installing and removing qt for a complete kde rebuild). + +* Building many packages in a row without recreating a chroot for every one of + them. + +* Knowing that any change you made to the chroot stays as you want (no one + touches your makepkg.conf) + +* Hability to run regular commands, not through a chroot wrapper. I can `cd` to + a dir and use `makepkg -whatever` on it and nothing breaks. + +* No extra code spent on wrappers. diff --git a/src/xbs-abs/.gitignore b/src/xbs-abs/.gitignore new file mode 100644 index 0000000..d472431 --- /dev/null +++ b/src/xbs-abs/.gitignore @@ -0,0 +1,3 @@ +commitpkg* +archrelease* +!*.patch diff --git a/src/xbs-abs/Makefile b/src/xbs-abs/Makefile new file mode 100644 index 0000000..248848f --- /dev/null +++ b/src/xbs-abs/Makefile @@ -0,0 +1,27 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkgconfdir = $(sysconfdir)/xbs +pkglibexecdir = $(libexecdir)/xbs + +_helpers = archrelease commitpkg +libretools-bins = +libretools-libexecs = helper-abs +pots += $(_helpers) +devtools-files += $(addsuffix .in,$(_helpers)) +am_out_files += $(_helpers) +am_sys_files += $(addprefix $(pkglibexecdir)/helper-abs.d/,$(_helpers)) + +$(outdir)/commitpkg: $(srcdir)/commitpkg.in + @echo "OUT $@" + @{ \ + echo '#!/usr/bin/env bash'; \ + echo '. "$$(librelib common)"'; \ + echo '. ./PKGBUILD'; \ + echo 'repo=$$1; arch=$$2;'; \ + sed -n "/== 'any'/,\$$p" $<; \ + } | install -Dm755 /dev/stdin $@ + +$(DESTDIR)$(pkglibexecdir)/helper-abs.d/%: $(srcdir)/% + install -Dm755 '$<' '$@' + +include $(topsrcdir)/automake.tail.mk diff --git a/src/xbs-abs/archrelease.patch b/src/xbs-abs/archrelease.patch new file mode 100644 index 0000000..bdf2deb --- /dev/null +++ b/src/xbs-abs/archrelease.patch @@ -0,0 +1,8 @@ +--- archrelease.in 2014-06-20 09:42:13.367553434 -0400 ++++ archrelease.ugly 2014-06-27 17:42:14.392917837 -0400 +@@ -1,4 +1,5 @@ + #!/bin/bash ++# License: Unspecified + + m4_include(lib/common.sh) + m4_include(lib/valid-tags.sh) diff --git a/src/xbs-abs/helper-abs b/src/xbs-abs/helper-abs new file mode 100755 index 0000000..d7e2499 --- /dev/null +++ b/src/xbs-abs/helper-abs @@ -0,0 +1,201 @@ +#!/usr/bin/env bash + +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> +# +# For code from db-functions (arch_svn): +# Copyright (C) 2012 Pierre Schmitz <pierre@archlinux.de> +# For code from db-move+db-remove (move+unrelease): +# Copyright (C) 2008-2009 Aaron Griffin <aaronmgriffin@gmail.com> +# Copyright (C) 2009 Abhishek Dasgupta <abhidg@gmail.com> +# Copyright (C) 2009, 2011 Dan McGee <dan@archlinux.org> +# Copyright (C) 2009-2012 Pierre Schmitz <pierre@archlinux.de> +# For code just from db-move (move): +# Copyright (C) 2011 Rémy Oudompheng <remyoudompheng@gmail.com> +# Copyright (C) 2012 Florian Pritz <bluewind@xinu.at> +# For code just from db-remove (unrelease): +# Copyright (C) 2009 Eric Bélanger <snowmaniscool@gmail.com> +# Copyright (C) 2011 Florian Pritz <bluewind@xinu.at> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +load_config() { + . "$(librelib conf.sh)" + load_files xbs-abs + # SVNUSER is optional + check_vars xbs-abs SVNDIR SVNREPOS ARCHES || exit 1 +} + +# This is taken from dbscripts:db-fuctions +arch_svn() { + if [[ -z "${SVNUSER}" ]]; then + /usr/bin/svn "${@}" + else + sudo -u "${SVNUSER}" -- /usr/bin/svn --username "${USER}" "${@}" + fi +} + +pac2svn() { + local pacrepo=$1 + + # Figure out which svn repo we need + local svnrepoStr + for svnrepoStr in "${SVNREPOS[@]}"; do + local svnrepoAry=($svnrepoStr) + local svnrepo=${svnrepoAry[0]} + local svnurl=${svnrepoAry[1]} + local pacrepos=("${svnrepoAry[@]:2}") + + if in_array "$pacrepo" "${pacrepos[@]}"; then + echo "$svnrepo" + return 0 + fi + done + return 1 +} + +status() { + load_config + [[ -z $(arch_svn status -q) ]] +} + +download() { + load_config + + local svnrepoStr + for svnrepoStr in "${SVNREPOS[@]}"; do + local svnrepoAry=($svnrepoStr) + local svnrepo=${svnrepoAry[0]} + local svnurl=${svnrepoAry[1]} + local pacrepos=("${svnrepoAry[@]:2}") + + if [[ -d "$SVNDIR/$svnrepo/.svn" ]]; then + arch_svn -q up "$SVNDIR/$svnrepo"/* + else + # checkout non-recursive, then lazy load + # necessary because: + # > DO NOT CHECK OUT THE ENTIRE SVN REPO. Your address + # > may be blocked. + arch_svn -q checkout -N "$svnurl" "$SVNDIR/$svnrepo" + fi + done +} + +release-client() { + local repo=$1 + local arch=$2 + + # Hack to use arch_svn as 'svn' in external scripts + local tmpdir="$(mktemp -dt "xbs-abs-release.XXXXXXXXXX")" + trap "$(printf 'rm -rf -- %q' "$tmpdir")" EXIT + printf '%s\n' \ + '#!/bin/bash' \ + "$(declare -p SVNUSER 2>/dev/null)" \ + "$(declare -f arch_svn)" \ + 'arch_svn "$@"' \ + > "$tmpdir/svn" + chmod 755 "$tmpdir/svn" + export PATH="$tmpdir:$PATH" + + "${0}.d/archrelease" -f "${repo}-${arch}" + "${0}.d/commitpkg" "${repo}" "${arch}" +} + +release-server() { + # Do nothing + : +} + +unrelease() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + local tag="$repo-$arch" + + load_config + local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}" + arch_svn up -q "$svndir" + + # This is based off code from dbscripts:db-remove + arch_svn rm --force -q "${svndir}/repos/${tag}" + arch_svn commit -q "${svndir}" -m "${0##*/}: $pkgbase removed by $(id -un)" +} + +move() { + local repo_from=$1 + local repo_to=$2 + local pkgbase=$3 + + load_config + local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}" + arch_svn up -q "$svndir" + + local tag_list="" + local pkgarch + local arches=() + # this is based off code from dbscripts:db-move + for pkgarch in "${ARCHES[@]}" 'any'; do + local dir_from="${svndir}/repos/${repo_from}-${pkgarch}" + local dir_to="${svndir}/repos/${repo_to}-${pkgarch}" + + if [ -f "${dir_from}/PKGBUILD" ]; then + if [ -d "${dir_to}" ]; then + for file in $(arch_svn ls "${dir_to}"); do + arch_svn rm -q "${dir_to}/$file@" + done + else + mkdir "${dir_to}" + arch_svn add -q "${dir_to}" + fi + + local file + for file in $(arch_svn ls "${dir_from}"); do + arch_svn mv -q -r HEAD "${dir_from}/$file@" "${dir_to}/" + done + arch_svn rm --force -q "${dir_from}" + tag_list="$tag_list, $pkgarch" + arches+=("$pkgarch") + fi + done + tag_list="${tag_list#, }" + arch_svn commit -q "${svndir}" -m "${0##*/}: moved ${pkgbase} from [${repo_from}] to [${repo_to}] (${tag_list})" + echo "${arches[*]}" +} + +releasepath() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + load_config + local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}" + arch_svn up -q "${svndir}" + local releasepath="${svndir}/repos/${repo}-${arch}" + if [[ -f "${releasepath}/PKGBUILD" ]]; then + printf '%s\n' "$releasepath" + return 0 + fi + return 1 +} + +name() { + echo SVN +} + +case "$1" in + status|download|release-client|release-server|unrelease|move|releasepath|name) "$@";; + *) exit 127;; +esac diff --git a/src/xbs-abs/xbs-abs.conf b/src/xbs-abs/xbs-abs.conf new file mode 100644 index 0000000..6b1585f --- /dev/null +++ b/src/xbs-abs/xbs-abs.conf @@ -0,0 +1,13 @@ +SVNDIR=/var/lib/xbs-abs +#SVNUSER= + +_packages_repos=(core extra testing staging {gnome,kde}-unstable) +_community_repos=({community,multilib}{,-testing,-staging}) + +# name url repos... +SVNREPOS=( + "packages svn://svn.archlinux.org/packages ${_packages_repos[*]}" + "community svn://svn.archlinux.org/community ${_community_repos[*]}" +) + +ARCHES=(i686 x86_64) diff --git a/src/xbs-abslibre/Makefile b/src/xbs-abslibre/Makefile new file mode 100644 index 0000000..c946d5d --- /dev/null +++ b/src/xbs-abslibre/Makefile @@ -0,0 +1,8 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkglibexecdir = $(libexecdir)/xbs + +libretools-bins = +libretools-libexecs = helper-abslibre + +include $(topsrcdir)/automake.tail.mk diff --git a/src/xbs-abslibre/helper-abslibre b/src/xbs-abslibre/helper-abslibre new file mode 100755 index 0000000..91e7361 --- /dev/null +++ b/src/xbs-abslibre/helper-abslibre @@ -0,0 +1,202 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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/>. + +. libremessages +setup_traps +set -u + +lockarch() { + local arch=$1 + lock 9 "${ABSLIBREDEST}/${arch}.lock" \ + "Waiting for a lock on the ABSLibre release directory for %s" "${arch}" +} + +unlockarch() { + lock_close 9 +} + +checkgit() { + if [[ ! -d "${ABSLIBREDEST}/${arch}/.git" ]]; then + error 'Not a git repository: %s' "${ABSLIBREDEST}/${arch}" + exit 1 + fi +} + +conf() { + . "$(librelib conf)" + load_files libretools + check_vars libretools "$@" || exit 1 +} + +################################################################################ + +# Operates on the `abslibre` format[0]. On the client/developer side, +# release-client *copies* files from the `abslibre` tree to an +# `abstree`[1] tree located at `devbox:${ABSLIBREDEST}/${arch}`. On +# the server side, after those `abstree`s have been uploaded +# (ssh/rsync), release-server *copies* them out of the `abstree` into +# a different `abslibre` on the server, similarly located at +# `repobox:${ABSLIBREDEST}/${arch}`. +# +# [0]: https://projects.parabola.nu/packages/pbs-tools.git/tree/docs/format-abslibre.md +# [1]: https://projects.parabola.nu/packages/pbs-tools.git/tree/docs/format-abstree.md + +# The number of arguments and CWD constraints are enforced by the main +# XBS program, no need to check those things here! + +# Args: none +# CWD: a directory with a PKGBUILD, in an 'abslibre' tree. +status() { + [[ -z $(git status -s .) ]] +} + +# Args: none +# CWD: anywhere +# Host: developer box +download() { + conf WORKDIR ABSLIBRERECV ABSLIBRESEND + + gitget -f -p "$ABSLIBRESEND" checkout "$ABSLIBRERECV" "$WORKDIR/abslibre" +} + +# Args: REPO ARCH +# CWD: a directory with a PKGBUILD, in an 'abslibre' tree. +# Host: developer box +release-client() { + local repo=$1 + local arch=$2 + + conf ABSLIBREDEST + local pkgbase="$(load_PKGBUILD >/dev/null; printf '%s\n' "${pkgbase:-${pkgname}}")" + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + lockarch "$arch" + + if [[ -e $pkgdir ]]; then + rm -rf -- "$pkgdir" + fi + + mkdir -p -- "$pkgdir" + git ls-files -z | xargs -0 -I{} cp -- {} "$pkgdir" +} + +# Args: REPO ARCH +# CWD: a directory with a PKGBUILD, outside of ${ABSLIBREDEST} +# Host: repo +release-server() { + local repo=$1 + local arch=$2 + + conf ABSLIBREDEST + local pkgbase="$(load_PKGBUILD >/dev/null; printf '%s\n' "${pkgbase:-${pkgname}}")" + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + lockarch "$arch" + checkgit + + if [[ -e $pkgdir ]]; then + rm -rf -- "$pkgdir" + fi + + mkdir -p -- "$pkgdir" + cp -- * "$pkgdir" + + cd "$pkgdir" + git add . + git commit -q -m "xbs-abslibre: Release ${repo}/${pkgbase} for ${arch} (by $(id -un))" +} + +# Args: PKGBASE REPO ARCH +# CWD: anywhere +# Host: repo +unrelease() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + conf ABSLIBREDEST + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + lockarch "$arch" + checkgit + + if [[ -f "${pkgdir}/PKGBUILD" ]]; then + git rm -qrf -- "$pkgdir" + git commit -q -m "xbs-abslibre: Remove ${repo}/${pkgbase} from ${arch} (by $(id -un))" + fi +} + +# Args: FROMREPO TOREPO PKGBASE +# CWD: anywhere +# Host: repo +move() { + local repo_from=$1 + local repo_to=$2 + local pkgbase=$3 + + conf ABSLIBREDEST ARCHES + + # Execute each iteration in a subshell so that 'checkgit' + # bailing for an architecture doesn't abort the entire thing. + local arch + for arch in "${ARCHES[@]}" any; do ( + lockarch "$arch" + checkgit + + local dir_from="${ABSLIBREDEST}/${arch}/${repo_from}/${pkgbase}" + local dir_to="${ABSLIBREDEST}/${arch}/${repo_to}/${pkgbase}" + + if [[ -f "${dir_from}/PKGBUILD" ]]; then + if [[ -e "${dir_to}" ]]; then + git rm -qrf -- "$dir_to" + fi + mkdir -p -- "${dir_to%/*}" + git mv -- "$dir_from" "$dir_to" + git commit -q -m "xbs-abslibre: Move ${pkgbase} from ${repo_from} to ${repo_to} on ${arch} (by $(id -un))" + fi + unlock arch + ) done +} + +# Args: PKGBASE REPO ARCH +# CWD: anywhere +# Host: repo +releasepath() { + local pkgbase=$1 + local repo=$2 + local arch=$3 + + conf ABSLIBREDEST + local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}" + + lockarch "$arch" + + if [[ -f "${pkgdir}/PKGBUILD" ]]; then + printf '%s\n' "$pkgdir" + return 0 + fi + + return 1 +} + +name() { + echo ABSLibre +} + +case "$1" in + status|download|release-client|release-server|unrelease|move|releasepath|name) "$@" || exit $?;; + *) exit 127;; +esac diff --git a/src/xbs/Makefile b/src/xbs/Makefile new file mode 100644 index 0000000..974586e --- /dev/null +++ b/src/xbs/Makefile @@ -0,0 +1,5 @@ +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/automake.head.mk +pkgconfdir = $(sysconfdir)/xbs + +include $(topsrcdir)/automake.tail.mk diff --git a/src/xbs/xbs b/src/xbs/xbs new file mode 100755 index 0000000..6319110 --- /dev/null +++ b/src/xbs/xbs @@ -0,0 +1,186 @@ +#!/usr/bin/env bash + +# Copyright (C) 2013-2015 Luke Shumaker <lukeshu@sbcglobal.net> +# +# License: GNU GPLv2+ +# +# This program 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 2 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_libdir=/usr/lib/xbs + +. libremessages +. "$(librelib conf)" + +errusage() { + if [[ $# -gt 0 ]]; then + error "$@" + fi + usage >&2 + exit 1 +} + +usage() { + print 'Usage: %s [-b BUILDSYSTEM|-h] COMMAND [ARGUMENTS]' "${0##*/}" + print 'Tool for working with arbitrary ABS-like build systems' + echo + prose 'This is a pluggable tool. The BUILDSYSTEM it uses is + configured in:' + bullet '/etc/xbs.conf' + bullet '${XDG_CONFIG_HOME}/xbs.conf' + bullet 'with the `-b` flag' + prose 'Later items take precedence over earlier ones.' + echo + prose 'It looks for a helper programs named helper-${BUILDSYSTEM}, in + the directory `%q` by default, but this directory can be changed + with the environmental variable XBS_LIBDIR.' "$default_libdir" + echo + print 'Options:' + flag "-b $(_ BUILDSYSTEM)" 'Use BUILDSYSTEM instead of the one + configured in xbs.conf' \ + '-h' 'Show this message' + echo + prose "Whether a command is intended for use on a developer's box or on + the server is indicated by (dev), (srv), or (any) before the + command" + echo + print 'Commands:' + flag "$(_ '(dev) status')" \ + 'Are there uncommitted changes in `.`?' \ + "$(_ '(dev) download')" \ + 'Download or update the tree' \ + "$(_ '(dev) release-client REPO ARCH')" \ + 'Release `.` (for developer boxes)' \ + "$(_ '(srv) release-server REPO ARCH')" \ + 'Release `.` (for server boxes)' \ + "$(_ '(srv) unrelease PKGBASE REPO ARCH')" \ + 'Unrelease a pkgbase' \ + "$(_ '(srv) move FROMREPO TOREPO PKGBASE')" \ + 'Move a pkgbase from one repo to another' \ + "$(_ '(srv) releasepath PKGBASE REPO ARCH')" \ + 'Print the path to the staged version of pkgbase, or exit with + non-zero if not released' \ + "$(_ '(any) name')" \ + 'Print a human-friendly version of the BUILDSYSTEM name' \ + "$(_ '(any) help')" \ + 'Show this message' +} + +status() { + if [[ ! -f PKGBUILD ]]; then + error 'PKGBUILD not found' + # Though in this file in general it doesn't matter, in + # this case it does: Using "exit" instead of "return" + # is imporant because it prevents flow returning to + # release-client. + exit 1 + fi + "$HELPER" status "$@" +} + +download() { + "$HELPER" download "$@" +} + +release-client() { + if ! status; then + error 'You have not committed your changes yet!' + exit 1 + fi + "$HELPER" release-client "$@" +} + +release-server() { + if [[ ! -f PKGBUILD ]]; then + error 'PKGBUILD not found' + exit 1 + fi + "$HELPER" release-server "$@" +} + +unrelease() { + "$HELPER" unrelease "$@" +} + +move() { + "$HELPER" move "$@" +} + +releasepath() { + "$HELPER" releasepath "$@" +} + +help() { + usage +} + +name() { + "$HELPER" name "$@" +} + +main() { + BUILDSYSTEM='' + while getopts 'b:h' arg; do + case $arg in + b) BUILDSYSTEM=$OPTARG;; + h) usage; return 0;; + *) errusage;; + esac + done + shift $(($OPTIND - 1)) + + if [[ -z $BUILDSYSTEM ]]; then + load_files xbs || return 1 + check_vars xbs BUILDSYSTEM || { + prose 'or specify the `-b` flag.' >&2 + return 1 + } + fi + + if [[ -z $XBS_LIBDIR ]]; then + export XBS_LIBDIR=$default_libdir + fi + + HELPER="${XBS_LIBDIR}/helper-${BUILDSYSTEM}" + if [[ ! -x "$HELPER" ]]; then + error 'No helper for build system found: %s' "$BUILDSYSTEM" + return 1; + fi + + if [[ $# -lt 1 ]]; then + errusage "Must specify a command" + fi + + if [[ -w / ]]; then + error 'Run as a normal user' + fi + + local cmd=$1; shift + case "$cmd" in + status|download|name|help) + [[ $# -eq 0 ]] || errusage 'bad number of argments' + "$cmd" "$@" + ;; + release-client|release-server) + [[ $# -eq 2 ]] || errusage 'bad number of argments' + "$cmd" "$@" + ;; + move|unrelease|releasepath) + [[ $# -eq 3 ]] || errusage 'bad number of argments' + "$cmd" "$@" + ;; + *) errusage 'unknown command: %s' "$cmd";; + esac +} + +main "$@" diff --git a/src/xbs/xbs.conf b/src/xbs/xbs.conf new file mode 100644 index 0000000..556c133 --- /dev/null +++ b/src/xbs/xbs.conf @@ -0,0 +1 @@ +BUILDSYSTEM=abslibre diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..2226ac6 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +.roundup.* diff --git a/test/aur-test.sh b/test/aur-test.sh new file mode 100644 index 0000000..5de590b --- /dev/null +++ b/test/aur-test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env roundup + +describe aur + +. ./test-common.sh + +before() { + _before +} + +after() { + _after +} + +it_displays_help() { + LC_ALL=C aur -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +it_fails_with_0_args() { + aur >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +# TODO: Actually test diff --git a/test/is_built-test.sh b/test/is_built-test.sh new file mode 100644 index 0000000..f7f6c65 --- /dev/null +++ b/test/is_built-test.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env roundup + +describe is_built + +. ./test-common.sh + +before() { + _before +} + +after() { + _after +} + +it_displays_help() { + LC_ALL=C is_built -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +it_fails_with_0_args() { + is_built >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat -gt 1 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_succeeds_with_1_arg() { + is_built sh >$tmpdir/stdout 2>$tmpdir/stderr + + empty $tmpdir/stdout + empty $tmpdir/stderr +} + +it_returns_1_for_non_existent_package() { + is_built phony-ne-package 100 >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat == 1 ]] + empty $tmpdir/stdout + empty $tmpdir/stderr +} + +it_returns_1_for_future_packages() { + # If emacs ever goes rapid release, we might need to change this :P + is_built emacs 100 >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat == 1 ]] + empty $tmpdir/stdout + empty $tmpdir/stderr +} + +it_returns_0_for_past_packages() { + # If emacs ever goes rapid release, we might need to change this :P + is_built emacs 1 >$tmpdir/stdout 2>$tmpdir/stderr + + empty $tmpdir/stdout + empty $tmpdir/stderr +} diff --git a/test/lib-blacklist-test.sh b/test/lib-blacklist-test.sh new file mode 100644 index 0000000..7b06f84 --- /dev/null +++ b/test/lib-blacklist-test.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env roundup + +describe libreblacklist + +. ./test-common.sh + +_blacklist_url=https://projects.parabola.nu/blacklist.git/plain/blacklist.txt + +before() { + _before +} + +after() { + _after +} + +it_works_with_just_pkgname() { + v="$(libreblacklist normalize <<<skype)"; [[ $v == 'skype::' ]] + v="$(libreblacklist get-pkg <<<skype)"; [[ $v == skype ]] + v="$(libreblacklist get-rep <<<skype)"; [[ -z $v ]] + v="$(libreblacklist get-reason <<<skype)"; [[ -z $v ]] +} + +it_works_with_everything_set() { + line='linux:linux-libre:nonfree blobs and firmwares' + v="$(libreblacklist normalize <<<"$line")"; [[ $v == "$line" ]] + v="$(libreblacklist get-pkg <<<"$line")"; [[ $v == 'linux' ]] + v="$(libreblacklist get-rep <<<"$line")"; [[ $v == 'linux-libre' ]] + v="$(libreblacklist get-reason <<<"$line")"; [[ $v == 'nonfree blobs and firmwares' ]] +} + +it_normalizes_correctly() { + v="$(libreblacklist normalize <<<pkg)"; [[ $v == 'pkg::' ]] + v="$(libreblacklist normalize <<<pkg:)"; [[ $v == 'pkg::' ]] + v="$(libreblacklist normalize <<<pkg::)"; [[ $v == 'pkg::' ]] + v="$(libreblacklist normalize <<<pkg:rep)"; [[ $v == 'pkg:rep:' ]] + v="$(libreblacklist normalize <<<pkg:rep:)"; [[ $v == 'pkg:rep:' ]] + v="$(libreblacklist normalize <<<pkg:rep:reason)"; [[ $v == 'pkg:rep:reason' ]] + v="$(libreblacklist normalize <<<pkg:rep:reason:)"; [[ $v == 'pkg:rep:reason:' ]] +} + +it_works_with_colons_in_reason() { + line='package:replacement:my:reason' + v="$(libreblacklist normalize <<<"$line")"; [[ $v == "$line" ]] + v="$(libreblacklist get-pkg <<<"$line")"; [[ $v == 'package' ]] + v="$(libreblacklist get-rep <<<"$line")"; [[ $v == 'replacement' ]] + v="$(libreblacklist get-reason <<<"$line")"; [[ $v == 'my:reason' ]] +} + +it_fails_update_with_no_blacklist_or_network() { + mkdir -p $XDG_CONFIG_HOME/libretools + echo "BLACKLIST='phony://example.com'" >$XDG_CONFIG_HOME/libretools/libretools.conf + + libreblacklist update >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_fails_cat_with_no_blacklist_or_network() { + mkdir -p $XDG_CONFIG_HOME/libretools + echo "BLACKLIST='phony://example.com'" >$XDG_CONFIG_HOME/libretools/libretools.conf + + libreblacklist cat >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_fails_update_when_BLACKLIST_is_unset() { + mkdir -p $XDG_CONFIG_HOME/libretools + echo "BLACKLIST=" >$XDG_CONFIG_HOME/libretools/libretools.conf + + libreblacklist update >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_fails_cat_when_syntax_error_in_conf() { + mkdir -p $XDG_CONFIG_HOME/libretools + # there is a stray single quote in there + printf "BLACKLIST='%q\n" "${_blacklist_url}" >$XDG_CONFIG_HOME/libretools/libretools.conf + + libreblacklist cat >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_downloads_the_blacklist_as_needed() { + require network || return 0 + mkdir -p $XDG_CONFIG_HOME/libretools + printf 'BLACKLIST=%q\n' "${_blacklist_url}" >$XDG_CONFIG_HOME/libretools/libretools.conf + + libreblacklist cat >$tmpdir/stdout 2>$tmpdir/stderr + + not empty $tmpdir/stdout +} + +it_downloads_the_blacklist_repeatedly() { + require network || return 0 + mkdir -p $XDG_CONFIG_HOME/libretools + printf 'BLACKLIST=%q\n' "${_blacklist_url}" >$XDG_CONFIG_HOME/libretools/libretools.conf + + libreblacklist update + libreblacklist update +} + +it_displays_help_and_fails_with_no_args() { + LC_ALL=C libreblacklist >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + [[ "$(sed 1q $tmpdir/stderr)" =~ 'Usage: libreblacklist ' ]] +} + +it_displays_help_when_given_h() { + LC_ALL=C libreblacklist -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ 'Usage: libreblacklist ' ]] + empty $tmpdir/stderr +} + +it_displays_help_when_given_h_cat() { + LC_ALL=C libreblacklist -h cat >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" == 'Usage: libreblacklist cat' ]] + empty $tmpdir/stderr +} diff --git a/test/lib-conf-test.sh b/test/lib-conf-test.sh new file mode 100644 index 0000000..efad907 --- /dev/null +++ b/test/lib-conf-test.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env roundup + +describe lib/conf.sh + +. ./test-common.sh + +before() { + _before test-conf.sh +} + +after() { + _after +} + +it_sets_makepkg_vars_in_custom_file() { + unset PKGDEST + touch "$tmpdir/makepkg.conf" + . $(librelib conf.sh) + MAKEPKG_CONF="$tmpdir/makepkg.conf" set_var makepkg PKGDEST /pkgdest + . "$tmpdir/makepkg.conf" + [[ $PKGDEST == /pkgdest ]] +} + +it_figures_out_HOME_when_root() { + require sudo || return 0 + # This one is tricky, because it does the job too well, it will find + # the actual HOME, instead of the test environment HOME. Therefore, we + # will just check that [[ $HOME != /root ]] + cd "$tmpdir" + echo '. $(librelib conf.sh); echo "$LIBREHOME"' > test.sh + LIBREHOME=$(testsudo bash ./test.sh) + [[ $LIBREHOME != /root ]] +} + +it_respects_custom_HOME() { + cd "$tmpdir" + echo '. $(librelib conf.sh); echo "$LIBREHOME"' > test.sh + + export HOME=/foo + LIBREHOME=$(bash ./test.sh) + + [[ $LIBREHOME == /foo ]] +} diff --git a/test/lib-messages-test.sh b/test/lib-messages-test.sh new file mode 100644 index 0000000..d895d99 --- /dev/null +++ b/test/lib-messages-test.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env roundup + +describe libremessages + +. ./test-common.sh + +before() { + _before +} + +after() { + _after +} + +it_can_be_included_twice() ( + . libremessages + . libremessages +) + +it_can_be_included_with_set_euE() ( + set -euE + . libremessages +) + +it_works_with_no_color_and_set_euE() ( + ( + unset TERM + set -euE + . libremessages + msg Foo + ) >$tmpdir/stdout 2>$tmpdir/stderr + + empty $tmpdir/stdout + not empty $tmpdir/stderr +) + +it_can_be_called_without_including() { + libremessages msg Foo >$tmpdir/stdout 2>$tmpdir/stderr + + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_fails_with_msg_and_no_args() { + ret=0 + libremessages msg || ret=$? + [[ $ret != 0 ]] +} + +it_allows_subheadings_to_flag() { + # Note that old versions of `flag` panicked if given an odd + # number of headings, so give an odd number here. + libremessages flag \ + -a adesc \ + -b bdesc \ + Head1: \ + -c cdesc > $tmpdir/out + printf '%s\n' \ + ' -a adesc' \ + ' -b bdesc' \ + ' Head1:' \ + ' -c cdesc' > $tmpdir/exp + diff -u $tmpdir/exp $tmpdir/out +} diff --git a/test/librechroot-test.sh b/test/librechroot-test.sh new file mode 100644 index 0000000..667246c --- /dev/null +++ b/test/librechroot-test.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env roundup + +describe librechroot + +. ./test-common.sh + +_setup_chrootdir + +before() { + _before librechroot + + mkdir -p "$XDG_CONFIG_HOME"/libretools + + echo "CHROOTDIR='${chrootdir}'" > "$XDG_CONFIG_HOME"/libretools/chroot.conf + echo "CHROOT='default'" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf + echo "CHROOTEXTRAPKG=()" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf +} + +after() ( + _after_sudo +) + +it_creates_repo_for_new_chroots() { + require network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo librechroot -l "$roundup_test_name" run test -r /repo/repo.db +} + +it_cleans_the_local_repo_correctly() { + require network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo librechroot -l "$roundup_test_name" make + testsudo librechroot -l "$roundup_test_name" clean-repo + testsudo librechroot -l "$roundup_test_name" run test -r /repo/repo.db + # TODO: inspect /repo/* more +} + +it_respects_exit_status_if_out_isnt_a_tty() ( + require network sudo || return 0 + set -o pipefail + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + r=0 + { testsudo librechroot -l "$roundup_test_name" run bash -c 'exit 3' | cat; } || r=$? + + [[ $r == 3 ]] +) + +it_creates_ca_certificates() { + require network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo librechroot -l "$roundup_test_name" run test -r /etc/ssl/certs/ca-certificates.crt +} + +it_disables_networking_when_requested() { + require network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + + testsudo librechroot -l "$roundup_test_name" run curl https://repo.parabola.nu/ >/dev/null + not testsudo librechroot -l "$roundup_test_name" -N run curl https://repo.parabola.nu/ >/dev/null +} + +it_handles_CHROOTEXTRAPKG_correctly() { + requuire network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + + not testsudo librechroot -l "$roundup_test_name" run lsof + echo "CHROOTEXTRAPKG=(lsof)" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf + testsudo librechroot -l "$roundup_test_name" install-name lsof + testsudo librechroot -l "$roundup_test_name" clean-pkgs + testsudo librechroot -l "$roundup_test_name" run lsof + echo "CHROOTEXTRAPKG=()" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf + testsudo librechroot -l "$roundup_test_name" clean-pkgs + not testsudo librechroot -l "$roundup_test_name" run lsof +} + +it_displays_help_as_normal_user() { + rm -rf "$XDG_CONFIG_HOME" + LC_ALL=C librechroot help >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +it_otherwise_fails_as_normal_user() { + librechroot -l "$roundup_test_name" run true >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_displays_help_and_fails_with_0_args() { + LC_ALL=C librechroot -l "$roundup_test_name" >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + [[ "$(sed -n 2p $tmpdir/stderr)" =~ Usage:.* ]] +} + +# requires sudo so we know it's not failing because it needs root +it_fails_for_unknown_commands() { + require sudo || return 0 + testsudo librechroot phony >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +# requires sudo so we know it's not failing because it needs root +it_fails_for_unknown_flags() { + require sudo || return 0 + testsudo librechroot -q >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_fails_when_syncing_a_copy_with_itself() { + require sudo || return 0 + testsudo timeout 5 librechroot -l root sync || stat=$? + case $stat in + 0|124|137) # success|timeout+TERM|timeout+KILL + false;; + *) + true;; + esac +} diff --git a/test/librefetch-test.sh b/test/librefetch-test.sh new file mode 100644 index 0000000..f10ee7f --- /dev/null +++ b/test/librefetch-test.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env roundup + +describe librefetch + +. ./test-common.sh + +KEYSERVER=hkp://pool.sks-keyservers.net +GPG="gpg --quiet --batch --no-tty --no-permission-warning --keyserver ${KEYSERVER}" + +before() { + _before + + mkdir -p "$XDG_CONFIG_HOME"/{pacman,libretools} "$tmpdir/srcdest" + + cat <<EOF > "$XDG_CONFIG_HOME/pacman/makepkg.conf" +DLAGENTS=('ftp::/usr/bin/curl -fC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u' + 'http::/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' + 'https::/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' + 'rsync::/usr/bin/rsync --no-motd -z %u %o' + 'scp::/usr/bin/scp -C %u %o') +BUILDDIR="" +SRCDEST=$tmpdir/srcdest +. ${_librelib_conf_sh_pkgconfdir}/librefetch-makepkg.conf +EOF + sed -i 's,/usr/bin/librefetch,$(which librefetch),' \ + "${_librelib_conf_sh_pkgconfdir}/librefetch-makepkg.conf" + + export MAKEPKG_CONF="$XDG_CONFIG_HOME/pacman/makepkg.conf" + + printf '%s\n' \ + 'MIRRORS=("phony://example.com/dir/")' \ + 'DOWNLOADER=/usr/bin/false' \ + > "$XDG_CONFIG_HOME/libretools/librefetch.conf" + + printf '%s\n' \ + 'Key-Type: RSA' \ + 'Key-Length: 1024' \ + 'Key-Usage: sign' \ + 'Name-Real: Temporary LibreTools testsuite key' \ + 'Name-Email: libretools-test@localhost' \ + 'Expire-Date: 0' \ + '%no-protection' \ + '%commit' \ + | $GPG --gen-key 2>/dev/null +} + +after() { + _after +} + +it_displays_help() { + LC_ALL=C librefetch -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +# This test used to be called "it_cleans_src_libre_first", but let's +# be honest: it checks pretty much everything related to normal +# operation. +it_runs() { + local srcball=testpkg-1.0.tar.gz + cp librefetch.d/* "$tmpdir/" + cd "$tmpdir" + + # Create garbage, to verifiy that it cleans src-libre first + mkdir -p src-libre/foo + touch src-libre/foo/file + + # Run librefetch + makepkg -g + + # Verify that no temporary files were left around + not test -e librefetch.* + + # Verify: + # - The srcball was created... + # - ... and is in the correct directory + # - The srcball does not contain the garbage created earlier + # - The files in the srcball are in the correct order (if the + # order isn't ensured, then this would only sometimes fail, + # unfortunately). + bsdtar tf "$tmpdir/srcdest/$srcball" > list-pkg.txt + diff -u list.txt list-pkg.txt + # Verify that the signature was created and matches + gpg --quiet --verify "$tmpdir/srcdest/$srcball"{.sig,} 2>/dev/null +} + +it_recurses() { + local srcball=testpkg-1.0.tar.gz + cp librefetch.d/* "$tmpdir/" + cd "$tmpdir" + mv PKGBUILD{-recurse,} + + makepkg -g + bsdtar tf "$tmpdir/srcdest/$srcball" > list-pkg.txt + diff -u list.txt list-pkg.txt + gpg --quiet --verify "$tmpdir/srcdest/$srcball"{.sig,} 2>/dev/null +} diff --git a/test/librefetch.d/PKGBUILD b/test/librefetch.d/PKGBUILD new file mode 100644 index 0000000..6547e25 --- /dev/null +++ b/test/librefetch.d/PKGBUILD @@ -0,0 +1,18 @@ +pkgname=testpkg +pkgver=1.0 +pkgrel=1 +pkgdesc=foo +arch=(any) +source=("libre://$pkgname-$pkgver.tar.gz"{,.sig}) + +mksource() { + mkdir "$srcdir/bar" + local file + for file in '~foo' '~a' a A; do + touch "$srcdir/bar/$file" + done +} + +package() { + :; +} diff --git a/test/librefetch.d/PKGBUILD-recurse b/test/librefetch.d/PKGBUILD-recurse new file mode 100644 index 0000000..fad5976 --- /dev/null +++ b/test/librefetch.d/PKGBUILD-recurse @@ -0,0 +1,18 @@ +pkgname=testpkg +pkgver=1.0 +pkgrel=1 +pkgdesc=foo +arch=(any) +source=("libre://$pkgname-$pkgver.tar.gz.sig") + +mksource() { + mkdir "$srcdir/bar" + local file + for file in '~foo' '~a' a A; do + touch "$srcdir/bar/$file" + done +} + +package() { + :; +} diff --git a/test/librefetch.d/list.txt b/test/librefetch.d/list.txt new file mode 100644 index 0000000..9bd32f4 --- /dev/null +++ b/test/librefetch.d/list.txt @@ -0,0 +1,5 @@ +bar/ +bar/A +bar/a +bar/~a +bar/~foo diff --git a/test/librelib-test.sh b/test/librelib-test.sh new file mode 100644 index 0000000..24c4478 --- /dev/null +++ b/test/librelib-test.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env roundup + +describe librelib + +. ./test-common.sh + +before() { + _before +} + +after() { + _after +} + +it_displays_help_and_fails_with_0_args() { + ret=0 + librelib >$tmpdir/stdout 2>$tmpdir/stderr || ret=$? + + empty $tmpdir/stdout + [[ "$(sed 1q $tmpdir/stderr)" =~ Usage:.* ]] + [[ $ret != 0 ]] +} + +it_fails_with_2_args() { + ret=0 + librelib a b >$tmpdir/stdout 2>$tmpdir/stderr || ret=$? + + empty $tmpdir/stdout + not empty $tmpdir/stderr + [[ $ret != 0 ]] +} + +it_displays_usage_text() { + librelib -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +# Nothing in $(libdir) should be executable anymore (except that +# $(libexecdir)=$(libdir), and executable things go in there. But I +# digress, libremessages should not be executable anymore). +it_finds_messages() { + v1=$(librelib messages) + v2=$(librelib libremessages) + v3=$(librelib messages.sh) + v4=$(librelib libremessages.sh) + + [[ -r "$v1" && ! -x "$v1" ]] + [[ "$v1" == "$v2" ]] + [[ "$v1" == "$v3" ]] + [[ "$v1" == "$v4" ]] +} + +# conf.sh is non-executable +it_finds_conf() { + v1=$(librelib conf) + v2=$(librelib libreconf) + v3=$(librelib conf.sh) + v4=$(librelib libreconf.sh) + + [[ -r "$v1" && ! -x "$v1" ]] + [[ "$v1" == "$v2" ]] + [[ "$v1" == "$v3" ]] + [[ "$v1" == "$v4" ]] +} + +it_fails_to_find_phony() { + ret=0 + librelib phony >$tmpdir/stdout 2>$tmpdir/stderr || ret=$? + + empty $tmpdir/stdout + not empty $tmpdir/stderr + [[ $ret != 0 ]] +} diff --git a/test/libremakepkg-test.sh b/test/libremakepkg-test.sh new file mode 100644 index 0000000..ddccab0 --- /dev/null +++ b/test/libremakepkg-test.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env roundup + +describe libremakepkg + +. ./test-common.sh + +_setup_chrootdir + +before() { + _before libremakepkg + + mkdir -p "$XDG_CONFIG_HOME"/libretools + + echo "BLACKLIST=https://repo.parabola.nu/docs/blacklist.txt" >"$XDG_CONFIG_HOME"/libretools/libretools.conf + + echo "CHROOTDIR='${chrootdir}'" > "$XDG_CONFIG_HOME"/libretools/chroot.conf + echo "CHROOT='default'" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf + echo "CHROOTEXTRAPKG=()" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf +} + +after() { + _after_sudo +} + +it_builds_a_trivial_package() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-hello "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo libremakepkg -l "$roundup_test_name" + + [[ -f $(echo libretools-hello-1.0-1-any.pkg.tar.?z) ]] +} + +it_enables_networking_during_prepare() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-netprepare "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo libremakepkg -l "$roundup_test_name" + [[ -f $(echo libretools-netprepare-1.0-1-any.pkg.tar.?z) ]] +} + +it_disables_networking_during_build() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-netbuild "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + not testsudo libremakepkg -l "$roundup_test_name" + not [[ -f $(echo libretools-netbuild-1.0-1-any.pkg.tar.?z) ]] + testsudo libremakepkg -l "$roundup_test_name" -N + [[ -f $(echo libretools-netbuild-1.0-1-any.pkg.tar.?z) ]] +} + +it_disables_networking_during_package() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-netpackage "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + not testsudo libremakepkg -l "$roundup_test_name" + not [[ -f $(echo libretools-netpackage-1.0-1-any.pkg.tar.?z) ]] + testsudo libremakepkg -l "$roundup_test_name" -N + [[ -f $(echo libretools-netpackage-1.0-1-any.pkg.tar.?z) ]] +} + +it_cleans_the_chroot_before_building() { + require network sudo || return 0 + # 1. First, we build testpkg1 + # 2. Then, we build testpkg2, which depends on testpkg1 + # Therefore, testpkg1 will be installed after testpkg2 is built, we + # check for that. + # 3. Then, we build hello, which depends on neither, so testpkg1 should + # be removed. + + # Also, do funny things with the output of libremakepkg to get a helpful + # fail case. + + mkdir -p "$tmpdir"/{1,2,3} + cp libremakepkg.d/PKGBUILD-testpkg1 "$tmpdir/1/PKGBUILD" + cp libremakepkg.d/PKGBUILD-testpkg2 "$tmpdir/2/PKGBUILD" + cp libremakepkg.d/PKGBUILD-hello "$tmpdir/3/PKGBUILD" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + + cd "$tmpdir/1" + testsudo libremakepkg -l "$roundup_test_name" &> "$tmpdir/out" || { r=$?; tail "$tmpdir/out"|cat -v; return $r; } + + cd "$tmpdir/2" + testsudo libremakepkg -l "$roundup_test_name" &> "$tmpdir/out" || { r=$?; tail "$tmpdir/out"|cat -v; return $r; } + testsudo librechroot -l "$roundup_test_name" run libretools-testpkg1 'first time, pass' + + # This next line is actually a separate test, but it fits in well with this test, and chroot tests are slow.. + # it_doesnt_cache_local_packages() { + not testsudo librechroot -l "$roundup_test_name" run test -e /var/cache/pacman/pkg/libretools-testpkg1-1.0-1-any.pkg.tar.?z + + cd "$tmpdir/3" + testsudo libremakepkg -l "$roundup_test_name" &> "$tmpdir/out" || { r=$?; tail "$tmpdir/out"|cat -v; return $r; } + not testsudo librechroot -l "$roundup_test_name" run libretools-testpkg1 'second time, fail' +} + +it_handles_PKGDEST_not_existing() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-hello "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo env PKGDEST="$tmpdir/dest/pkgdest" libremakepkg -l "$roundup_test_name" + + [[ -f $(echo dest/pkgdest/libretools-hello-1.0-1-any.pkg.tar.?z) ]] +} + +it_displays_help_as_normal_user() { + rm -rf "$XDG_CONFIG_HOME" + LC_ALL=C libremakepkg -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +it_otherwise_fails_as_normal_user() { + # I do this to give it a chance of passing + cp libremakepkg.d/PKGBUILD-hello "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremakepkg >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} diff --git a/test/libremakepkg.d/PKGBUILD-hello b/test/libremakepkg.d/PKGBUILD-hello new file mode 100644 index 0000000..5f320fe --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-hello @@ -0,0 +1,19 @@ +pkgname='libretools-hello' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) +depends=(sh) + +build() { + cd "$srcdir" + echo '#!/bin/sh' > hello.sh + echo 'echo Hello, world!' >> hello.sh +} + +package() { + cd "$srcdir" + install -Dm755 hello.sh "$pkgdir"/usr/bin/libretools-hello +} diff --git a/test/libremakepkg.d/PKGBUILD-netbuild b/test/libremakepkg.d/PKGBUILD-netbuild new file mode 100644 index 0000000..4db1274 --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-netbuild @@ -0,0 +1,17 @@ +pkgname='libretools-netbuild' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) + +build() { + cd "$srcdir" + curl https://repo.parabola.nu/ > index.html +} + +package() { + cd "$srcdir" + install -Dm644 index.html "$pkgdir"/usr/share/$pkgname/index.html +} diff --git a/test/libremakepkg.d/PKGBUILD-netpackage b/test/libremakepkg.d/PKGBUILD-netpackage new file mode 100644 index 0000000..6cadcf8 --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-netpackage @@ -0,0 +1,12 @@ +pkgname='libretools-netpackage' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) + +package() { + install -d "$pkgdir"/usr/share/$pkgname + curl https://repo.parabola.nu/ > "$pkgdir"/usr/share/$pkgname/index.html +} diff --git a/test/libremakepkg.d/PKGBUILD-netprepare b/test/libremakepkg.d/PKGBUILD-netprepare new file mode 100644 index 0000000..efb7a43 --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-netprepare @@ -0,0 +1,17 @@ +pkgname='libretools-netprepare' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) + +prepare() { + cd "$srcdir" + curl https://repo.parabola.nu/ > index.html +} + +package() { + cd "$srcdir" + install -Dm644 index.html "$pkgdir"/usr/share/$pkgname/index.html +} diff --git a/test/libremakepkg.d/PKGBUILD-testpkg1 b/test/libremakepkg.d/PKGBUILD-testpkg1 new file mode 100644 index 0000000..8da1f14 --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-testpkg1 @@ -0,0 +1,19 @@ +pkgname='libretools-testpkg1' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) +depends=(sh) + +build() { + cd "$srcdir" + echo '#!/bin/sh' > testpkg1.sh + echo 'echo testpkg1' >> testpkg1.sh +} + +package() { + cd "$srcdir" + install -Dm755 testpkg1.sh "$pkgdir"/usr/bin/libretools-testpkg1 +} diff --git a/test/libremakepkg.d/PKGBUILD-testpkg2 b/test/libremakepkg.d/PKGBUILD-testpkg2 new file mode 100644 index 0000000..65d558e --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-testpkg2 @@ -0,0 +1,19 @@ +pkgname='libretools-testpkg2' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) +depends=(sh libretools-testpkg1) + +build() { + cd "$srcdir" + echo '#!/bin/sh' > testpkg2.sh + echo 'libretools-testpkg1' >> testpkg2.sh +} + +package() { + cd "$srcdir" + install -Dm755 testpkg2.sh "$pkgdir"/usr/bin/libretools-testpkg2 +} diff --git a/test/librerelease-test.sh b/test/librerelease-test.sh new file mode 100644 index 0000000..a44b150 --- /dev/null +++ b/test/librerelease-test.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env roundup + +describe librestage + +. ./test-common.sh + +before() { + _before + + mkdir -p $XDG_CONFIG_HOME/libretools + { + echo "WORKDIR='$tmpdir/workdir'" + echo 'REPODEST=repo@repo:/srv/http/repo/staging-$LIBREUSER' + } >$XDG_CONFIG_HOME/libretools/libretools.conf + { + echo 'PKGEXT=.pkg.tar.gz' + echo "PKGDEST='$tmpdir/workdir/pkgdest'" + echo "GPGKEY=YOURKEY" + } > $HOME/.makepkg.conf + mkdir -p "$tmpdir/workdir/pkgdest" +} + +after() { + _after +} + +it_displays_usage_text() { + rm -rf "$XDG_CONFIG_HOME" + LC_ALL=C librerelease -h >"$tmpdir/stdout" 2>"$tmpdir/stderr" + + [[ "$(sed 1q "$tmpdir/stdout")" =~ Usage:.* ]] + empty "$tmpdir/stderr" +} + +it_lists_all_files() { + WORKDIR="$tmpdir/workdir" + mkdir -p "$WORKDIR/staging/repo1" "$WORKDIR/staging/repo2/sub" + touch \ + "$WORKDIR/staging/repo1/file1" \ + "$WORKDIR/staging/repo1/file2" \ + "$WORKDIR/staging/repo2/file with spaces" \ + "$WORKDIR/staging/repo2/sub/subfolder" + unset WORKDIR + LC_ALL=C librerelease -l &>"$tmpdir/list" + + cat > "$tmpdir/list-correct" <<EOF + -> repo1 + file1 + file2 + -> repo2 + file with spaces + sub/subfolder +EOF + + diff "$tmpdir/list-correct" "$tmpdir/list" +} diff --git a/test/librestage-test.sh b/test/librestage-test.sh new file mode 100644 index 0000000..affad6a --- /dev/null +++ b/test/librestage-test.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env roundup + +describe librestage + +. ./test-common.sh + +before() { + _before + + mkdir -p $XDG_CONFIG_HOME/libretools + echo "WORKDIR='$tmpdir/workdir'" > $XDG_CONFIG_HOME/libretools/libretools.conf + echo "ARCHES=('x86_64' 'i686' 'misp64el')" >> $XDG_CONFIG_HOME/libretools/libretools.conf + + mkdir -p $XDG_CONFIG_HOME/xbs + echo "BUILDSYSTEM=abslibre" > $XDG_CONFIG_HOME/xbs/xbs.conf + + echo 'PKGEXT=.pkg.tar.gz' > $HOME/.makepkg.conf + echo "PKGDEST='$tmpdir/workdir/pkgdest'" >> $HOME/.makepkg.conf + echo "PACKAGER='Test Suite <test@localhost>'" >> $HOME/.makepkg.conf + mkdir -p "$tmpdir/workdir/pkgdest" +} + +after() { + _after +} + +it_displays_usage_text() { + rm -rf "$XDG_CONFIG_HOME" + LC_ALL=C librestage -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q "$tmpdir/stdout")" =~ Usage:.* ]] + empty "$tmpdir/stderr" +} + +it_fails_with_0_args() { + librestage >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty "$tmpdir/stdout" + not empty "$tmpdir/stderr" +} + +it_fails_with_invalid_args() { + librestage -q >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + [[ $stat != 0 ]] + empty "$tmpdir/stdout" + not empty "$tmpdir/stderr" +} + +it_guesses_the_repo() { + mkdir -p -- "$tmpdir/reponame/libretools-hello" + cp librestage.d/PKGBUILD-hello "$tmpdir/reponame/libretools-hello/PKGBUILD" + cd "$tmpdir/reponame/libretools-hello" + + makepkg + librestage + + [[ -f $tmpdir/workdir/staging/reponame/libretools-hello-1.0-1-any.pkg.tar.gz ]] +} + +it_stages_packages_without_PKGDEST() { + echo "PKGDEST=''" >> $HOME/.makepkg.conf + + cp librestage.d/PKGBUILD-hello "$tmpdir/PKGBUILD" + cd "$tmpdir" + + makepkg + librestage repo1 + + [[ -f $tmpdir/workdir/staging/repo1/libretools-hello-1.0-1-any.pkg.tar.gz ]] +} diff --git a/test/librestage.d/PKGBUILD-hello b/test/librestage.d/PKGBUILD-hello new file mode 100644 index 0000000..5f320fe --- /dev/null +++ b/test/librestage.d/PKGBUILD-hello @@ -0,0 +1,19 @@ +pkgname='libretools-hello' +pkgver=1.0 +license=('GPL') +url='https://parabola.nu' + +pkgrel=1 +arch=(any) +depends=(sh) + +build() { + cd "$srcdir" + echo '#!/bin/sh' > hello.sh + echo 'echo Hello, world!' >> hello.sh +} + +package() { + cd "$srcdir" + install -Dm755 hello.sh "$pkgdir"/usr/bin/libretools-hello +} diff --git a/test/pkgbuild-check-nonfree-test.sh b/test/pkgbuild-check-nonfree-test.sh new file mode 100644 index 0000000..2af2669 --- /dev/null +++ b/test/pkgbuild-check-nonfree-test.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env roundup + +# avoid carpel tunnel +pcn=pkgbuild-check-nonfree +psn=pkgbuild-summarize-nonfree + +describe $pcn + +. ./test-common.sh + +before() { + _before + + mkdir -p $XDG_CONFIG_HOME/libretools + echo "BLACKLIST='phony://example.com'" >$XDG_CONFIG_HOME/libretools/libretools.conf + + local blacklist=$XDG_CACHE_HOME/libretools/blacklist.txt + mkdir -p "${blacklist%/*}" + echo 'linux:linux-libre:nonfree blobs and firmwares' >$blacklist + echo 'skype' >>$blacklist +} + +after() { + _after +} + +it_displays_usage_text() { + # This test seems silly, but it makes sure that it is executable, + # syntactically correct, and loading libraries works. + LC_ALL=C $pcn -h >$tmpdir/stdout 2>$tmpdir/stderr + stat=$? + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr + [[ $stat == 0 ]] +} + +it_succeeds_for_free_depends() { + $pcn $pcn.d/PKGBUILD.free >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + empty $tmpdir/stdout + not empty $tmpdir/stderr + [[ $stat == 0 ]] +} + +it_succeeds_for_nonfree_depend_with_replacement() { + $pcn $pcn.d/PKGBUILD.nonfree-replacement >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + empty $tmpdir/stdout + not empty $tmpdir/stderr + [[ $stat == 0 ]] +} + +it_fails_for_nonfree_depend() { + $pcn $pcn.d/PKGBUILD.nonfree >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr + + local pcn_stat=$stat + + $psn $pcn_stat >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr +} + +it_fails_when_there_is_no_blacklist() { + mkdir -p $XDG_CONFIG_HOME/libretools + echo "BLACKLIST='phony://example.com'" >$XDG_CONFIG_HOME/libretools/libretools.conf + rm $XDG_CACHE_HOME/libretools/blacklist.txt + + $pcn $pcn.d/PKGBUILD.free >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + + empty $tmpdir/stdout + not empty $tmpdir/stderr + [[ $stat != 0 ]] && [[ $stat != 15 ]] +} diff --git a/test/pkgbuild-check-nonfree.d/PKGBUILD.free b/test/pkgbuild-check-nonfree.d/PKGBUILD.free new file mode 100644 index 0000000..4b8f0dd --- /dev/null +++ b/test/pkgbuild-check-nonfree.d/PKGBUILD.free @@ -0,0 +1,18 @@ +pkgname=wmii +pkgver=3.9.2 +pkgrel=3 +pkgdesc="A small, dynamic window manager for X11" +arch=('i686' 'x86_64') +license=('MIT') +url="http://wmii.suckless.org/" +depends=('libxft' 'libxrandr' 'libxinerama' 'dash') +source=() +md5sums=() + +build() { + : +} + +package() { + : +} diff --git a/test/pkgbuild-check-nonfree.d/PKGBUILD.nonfree b/test/pkgbuild-check-nonfree.d/PKGBUILD.nonfree new file mode 100644 index 0000000..3a7afa4 --- /dev/null +++ b/test/pkgbuild-check-nonfree.d/PKGBUILD.nonfree @@ -0,0 +1,18 @@ +pkgname=wmii +pkgver=3.9.2 +pkgrel=3 +pkgdesc="A small, dynamic window manager for X11" +arch=('i686' 'x86_64') +license=('MIT') +url="http://wmii.suckless.org/" +depends=('skype') # random non-free package with no other information +source=() +md5sums=() + +build() { + : +} + +package() { + : +} diff --git a/test/pkgbuild-check-nonfree.d/PKGBUILD.nonfree-replacement b/test/pkgbuild-check-nonfree.d/PKGBUILD.nonfree-replacement new file mode 100644 index 0000000..7855bdc --- /dev/null +++ b/test/pkgbuild-check-nonfree.d/PKGBUILD.nonfree-replacement @@ -0,0 +1,18 @@ +pkgname=wmii +pkgver=3.9.2 +pkgrel=3 +pkgdesc="A small, dynamic window manager for X11" +arch=('i686' 'x86_64') +license=('MIT') +url="http://wmii.suckless.org/" +depends=('linux') # random non-free package with a replacement +source=() +md5sums=() + +build() { + : +} + +package() { + : +} diff --git a/test/test-common.sh b/test/test-common.sh new file mode 100644 index 0000000..2a8fc37 --- /dev/null +++ b/test/test-common.sh @@ -0,0 +1,78 @@ +#!/hint/bash + +if [[ $HOME == "$(eval echo ~$USER)" ]]; then + libremessages error "\$HOME is the default for %s; use testenv: %s" "$USER" "$HOME" + exit 1 +fi + +_before() { + unset PKGDEST SRCDEST SRCPKGDEST LOGDEST + unset BUILDDIR + unset PKGEXT SRCEXT + unset GPGKEY PACKAGER + tmpdir="$(mktemp -d --tmpdir "test-${roundup_desc//\//-}.XXXXXXXXXXXX")" + chmod 755 "$tmpdir" + stat=0 +} + +_after() { + rm -rf -- "$tmpdir" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" +} + +_after_sudo() { + if [[ $SUDO ]]; then + sudo rm -rf -- "$tmpdir" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" + else + rm -rf -- "$tmpdir" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" + fi +} + +_setup_chrootdir() { + if [[ -z "$chrootdir" ]]; then + export chrootdir="$(mktemp -d --tmpdir "test-chrootdir.XXXXXXXXXXXX")" + trap "$(printf '_cleanup_chrootdir %q' "$chrootdir")" EXIT + fi +} + +_cleanup_chrootdir() ( + chrootdir=$1 + shopt -s nullglob + if [[ $SUDO ]]; then + for copydir in "$chrootdir"/*/*/; do + local chroottype=$(stat -f -c %T "$copydir") + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then + sudo btrfs subvolume delete "$copydir" >/dev/null + fi + done + sudo rm -rf -- "$chrootdir" + else + rm -rf -- "$chrootdir" + fi +) + +require() ( + set +x + local missing=() + if libremessages in_array "network" "$@" && ! [[ $NETWORK ]]; then + missing+=('networking') + fi + if libremessages in_array "sudo" "$@" && ! [[ $SUDO ]]; then + missing+=('sudo') + fi + if (( ${#missing[@]} )); then + libremessages warning "Next test requires %s; Skipping (passing)..." "$(echo "${missing[*]}"|sed 's/ /, /g')" &>/dev/tty + return 1 + fi + return 0; +) + +empty() ( + set +x + [[ $(stat -c %s "$1") -eq 0 ]] +) + +# Just using '!' doesn't trip `set -e` +not() ( + set +x + ! eval "$@" +) diff --git a/test/testenv b/test/testenv new file mode 100755 index 0000000..7072326 --- /dev/null +++ b/test/testenv @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Parse the arguments +NETWORK=true +SUDO=true +while [[ $# -gt 0 ]]; do + case "$1" in + --no-network) shift; unset NETWORK;; + --network) shift; NETWORK=true;; + --no-sudo) shift; unset SUDO;; + --sudo) shift; SUDO=true;; + --) shift; break;; + *) break;; + esac +done +export NETWORK SUDO + +if [[ $# == 0 ]]; then + echo 'You need to run testenv with arguments!' >&2 + exit 1 +fi + +# Set up the working directory, and add the hook to clean it up +export TMPDIR="$(mktemp --tmpdir -d libretools-test.XXXXXXXXXX)" +trap "rm -rf '$TMPDIR'" EXIT + +# Set up the install to work with +destdir=$TMPDIR/destdir + +old_pwd="$(pwd)" +if [[ -f $0 ]]; then + cd "${0%/*}" +fi +if ! make -C .. install DESTDIR="$destdir" &>"$TMPDIR/make-output"; then + echo 'error creating local install, cannot run tests' >&2 + cat "$TMPDIR/make-output" + exit 1 +fi +cd "$old_pwd" + +# Set up the environment +export PATH="$destdir/usr/bin:$PATH" +export LIBRETOOLS_LIBDIR="$destdir/usr/lib/libretools" +export XBS_LIBDIR="$destdir/usr/lib/xbs" +export HOME=$TMPDIR/home +export XDG_CACHE_HOME="$HOME/.cache" +export XDG_CONFIG_HOME="$HOME/.config" +export _librelib_conf_sh_sysconfdir="$destdir/etc" +export _librelib_conf_sh_pkgconfdir="$destdir/etc/libretools.d" + +# Hack to respect our variables in sudo +_sudo() { + local vars=(TMPDIR PATH LIBRETOOLS_LIBDIR XDG_CACHE_HOME XDG_CONFIG_HOME _librelib_conf_sh_sysconfdir) + local args=() + local var + for var in "${vars[@]}"; do + args+=("$var=${!var}") + done + sudo env "${args[@]}" "$@" +} +printf '%s\n' \ + '#!/bin/bash' \ + "$(declare -f _sudo)" \ + '_sudo "$@"' \ + > "$destdir/usr/bin/testsudo" +chmod 755 "$destdir/usr/bin/testsudo" + +# Run the tests +eval "$@" diff --git a/write-ifchanged b/write-ifchanged new file mode 100755 index 0000000..c65fa16 --- /dev/null +++ b/write-ifchanged @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +outfile=$1 +tmpfile="$(dirname "$outfile")/.tmp${outfile##*/}" + +cat > "$tmpfile" || exit $? +if cmp -s "$tmpfile" "$outfile"; then + rm -f "$tmpfile" || : +else + mv -f "$tmpfile" "$outfile" +fi |