gjp 0.15.7 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,44 +1,204 @@
1
- gjp – Green Java Packager's tools
2
- ===
1
+ # gjp – Green Java Packager's tools
3
2
 
4
3
  `gjp` (pronounced _/ˈdʒiː ˈaɪ ˈd͡ʒəʊ/_) is a set of tools to ease and partially automate Linux packaging of Java projects.
5
4
 
6
- The project focus is on producing rpm packages for SUSE distributions, but it is general enough to be useful even for other distributions.
5
+ The project objective is to strongly reduce manual packaging efforts by enabling a new and much simpler workflow.
6
+
7
+ ## Status
8
+
9
+ `gjp` is a research project currently in alpha state. Basic concepts seem to be viable, packages are currently being built with the new approach to identify problem areas but not all basic features have been coded yet. If you are a packager you can try to use it (any feedback would be **very** welcome!), but be warned that anything can still change at this point.
10
+
11
+ ## Contact
12
+
13
+ Are you using `gjp` or even just reading this? Let's get in touch!
14
+
15
+ smoioli at suse dot de
7
16
 
8
17
 
9
18
  ## Install
10
19
 
11
- Easiest install is via RubyGems:
20
+ Via RubyGems:
12
21
 
13
22
  $ gem install gjp
14
23
 
15
- ## Usage
16
-
17
- Main workflow subcommands:
18
- * `gjp init` inits a new gjp project in the current directory, generating minimal directories and files;
19
- * `gjp gather` starts a gathering phase, to add source and kit files. You should place source files in src/<package name> and binary dependency files in kit/;
20
- * `gjp dry-run` starts a dry-run phase, to attempt a build. Any change to src/ will be reverted after you call `gjp finish`;
21
- * `gjp mvn` during a dry run, locates and runs Maven from any directory in kit/, using options to force repository in kit/m2 and settings in kit/m2/settings.xml;
22
- * `gjp status` prints the current phase;
23
- * `gjp finish` ends the current phase;
24
- * `gjp generate-kit-spec` creates or refreshes a spec file for the kit. Use after `gjp finish`;
25
- * `gjp generate-kit-archive` creates or refreshes an archive file for the kit. Use after `gjp finish`;
26
- * `gjp generate-package-spec NAME POM` creates or refreshes a spec file for the package in src/<NAME>. Use after `gjp finish`;
27
- * `gjp generate-package-archive NAME` creates or refreshes an archive file for package in src/<NAME>. Use after `gjp finish`;
28
-
29
- Optional workflow subcommands:
30
- * `gjp set-up-nonet-user` sets up a user named `nonet` without Internet access you can use for networkless dry runs. Requires `iptables` and
31
- superuser privileges;
32
- * `gjp tear-down-nonet-user` removes a user previously created by gjp;
33
-
34
- Other available tools:
24
+ Note: `gjp` requires `git` in order to work. Some non-essential subcommands also need `iptables`, `sudo` and a JDK.
25
+
26
+ ## Workflow
27
+
28
+ Building a package with `gjp` is quite unusual this is a [deliberate choice](#motivation) to minimize packaging efforts.
29
+
30
+ ### Overview
31
+
32
+ The basic process is:
33
+
34
+ * a `gjp` project is created;
35
+ * sources are added to the project, `gjp` keeps track of them;
36
+ * any other file that is needed for the build, except the JDK, is added in binary form (jars, the Maven executable, its plugins, etc.). Again, `gjp` keeps track of what you add, noting that those were binary build dependencies and not sources;
37
+ * a build is attempted. After the build is successful, `gjp` notes what files were produced and restores sources in their original state, making it a "repeatable dry-run build". `gjp` also retains any files that were automatically downloaded by Maven or other similar tools during the dry-run as binary build dependencies;
38
+ * `gjp` produces spec files for two packages: one for the project itself and one for all of its binary build dependencies, called a **kit**;
39
+ * kit and project packages can be submitted to [OBS](http://en.opensuse.org/openSUSE:Build_Service). Project package will rebuild cleanly because it needs no Internet access - all files were already downloaded during the dry-run above and are included in the kit.
40
+
41
+ Note that:
42
+
43
+ * the project's build time dependency graph is very simple: just its kit and the JDK;
44
+ * the kit is basically a binary blob. If its sources are needed for proper packaging, for example to comply with the GPL, [a separate step](#kit-sources) is needed to add them;
45
+ * the kit is needed at build time only (by OBS), no end user should ever install it;
46
+ * a `gjp` project can be used to build a number of packages that share one binary kit. This can help if the kit becomes big in size;
47
+ * `gjp` will take advantage of Maven's pom files to generate its specs if they are available. This allows to precompile most spec fields automatically.
48
+
49
+ In `gjp`, the build process can be in one of the following phases at any given moment:
50
+
51
+ * gathering: in this phase you add sources and kit files. New projects start in this phase, you can always enter it later with `gjp gather`;
52
+ * dry-running: in this phase you attempt a build. Any change in the sources will be reverted after it ends, while files added to the kit will be retained. You can enter this phase with `gjp dry-run`;
53
+ * finishing: in this phase `gjp` generates specs and archive files. You can enter it running `gjp finish`;
54
+
55
+ ### Sample project (commons-io)
56
+
57
+ Ceate a new `gjp` project, in this example named "galaxy":
58
+
59
+ mkdir galaxy
60
+ cd galaxy
61
+ gjp init
62
+
63
+ As you can see from the output, `gjp init` starts a new gathering phase in which you can add sources and kit files. It also generated a folder structure and assumes you respect it, in particular, you should place all your projects' source files in `src`. Every `src` subfolder will become a separate package named after the folder itself, so use the following commands to create a `commons-collections` folders and populate it:
64
+
65
+ cd src
66
+ mkdir commons-collections
67
+ cd commons-collections
68
+ wget http://archive.apache.org/dist/commons/collections/source/commons-collections-3.2.1-src.zip
69
+ unzip commons-collections-3.2.1-src.zip
70
+ rm commons-collections-3.2.1-src.zip
71
+
72
+ Now let's move to the kit (which, unsurprisingly, should be placed in the `kit` folder). commons-lang needs Maven 3 to build, so we should simply unzip a copy in `kit`:
73
+
74
+ cd ../../kit
75
+ wget http://apache.fastbull.org/maven/maven-3/3.1.0/binaries/apache-maven-3.1.0-bin.zip
76
+ unzip apache-maven-3.1.0-bin.zip
77
+ rm apache-maven-3.1.0-bin.zip
78
+ cd ..
79
+
80
+ Nothing else is needed for a first build. Let's call `gjp dry-run` to let `gjp` know we are building and then call Maven. Note that `gjp mvn` is used instead of plain `mvn`: `gjp` will take care of locating the Maven installation we have in the `kit` and ensure it will store all downloaded files there.
81
+
82
+ gjp dry-run
83
+ cd src/commons-collections/commons-collections-3.2.1-src/
84
+ gjp mvn package
85
+ gjp finish
86
+
87
+ Success! At this point `gjp` took note of all needed files, and restored `src` as it was before the build. This should be sufficient to be able to repeat the build on a machine with no Internet access, but what if we wanted to be 100% sure of that?
88
+
89
+ `gjp` has a subcommand to setup a `nonet` user without Internet access, courtesy of `iptables`. You can simply retry the build using that user to see if it works. Note that the following commands will alter group permissions to allow both your current user and `nonet` to work on the same files.
90
+
91
+ gjp set-up-nonet-user
92
+ chmod -R g+rw ../../..
93
+ gjp dry-run
94
+ su nonet
95
+ ping www.google.com #this should fail!
96
+ gjp mvn package
97
+ chmod -R g+rw .
98
+ exit
99
+
100
+ The above is obviously not mandatory, but it can be useful for debugging purposes.
101
+
102
+ One last thing before generating packages is to setup a build script. By default `gjp` will generate a spec file which assumes a `build.sh` script in the source folder of your project that contains all commands needed to build the package itself. At the moment, this needs to be done manually, but it will hopefully be automated in a future release.
103
+
104
+ Let's start a new gathering phase and add that:
105
+
106
+ gjp gather
107
+ vi ../build.sh
108
+
109
+ Add the following lines:
110
+
111
+ #!/bin/sh
112
+ cd src/commons-collections/commons-collections-3.2.1-src/
113
+ ../../../kit/apache-maven-3.1.0/bin/mvn -Dmaven.repo.local=`readlink -e ../../../kit/m2` -s`readlink -e ../../../kit/m2/settings.xml` package
114
+
115
+ Now complete the gathering:
116
+
117
+ gjp finish
118
+ cd ../../..
119
+
120
+ Note that `build.sh` gets called from the `gjp` project root, hence the `cd` line, and the Maven line was taken directly from `gjp mvn` output above and pasted verbatim.
121
+
122
+ The following command will generate the kit spec:
123
+
124
+ gjp generate-kit-spec
125
+ less specs/galaxy-kit.spec
126
+
127
+ Nothing fancy here, just a bunch of binaries installed in a proper location. You can also edit the spec file manually if you want. When you later regenerate it, `gjp` will automatically try to reconcile changes with a [three-way merge](http://en.wikipedia.org/wiki/Three-way_merge#Three-way_merge).
128
+
129
+ You can also generate the corresponding .tar.xz file with:
130
+
131
+ gjp generate-kit-archive
132
+
133
+ The contents of this file were tracked by `gjp` during gathering and dry-run phases, and are listed in `file_lists/kit`. You can also edit it if you want.
134
+
135
+ You can then generate the project spec and archive files provided you have a pom file (more formats will be supported in future). In this case:
136
+
137
+ gjp generate-package-spec commons-collections src/commons-collections/commons-collections-3.2.1-src/pom.xml
138
+ gjp generate-package-archive commons-collections
139
+ less specs/commons-collections.spec
140
+
141
+ As you can see, this package BuildRequires its kit, contains only the source files and installs any .jar files that were produced during dry runs. Archive is generated from `file_lists/commons-collections_input`, which lists source files. Output files are in `file_lists/commons-collections_output` and are used to compile the `%install` and `%files` sections of the project spec (by default jar files are included, see `gjp generate-package-spec --help`).
142
+
143
+ less file_lists/commons-collections_input
144
+ less file_lists/commons-collections_output
145
+
146
+ Packages are ready to be submitted to an OBS project. As OBS integration is not yet implemented, refer to OBS documentation to do that.
147
+
148
+ ### Kit sources
149
+
150
+ If kit sources are needed for license compliance, some extra work is needed. Fortunately, finding jar source files and adding them to the kit is much easier than packaging its contents in proper RPMs!
151
+
152
+ If the project you are packaging uses Maven, you can ask Maven itself to find source jars for dependencies. Running the following command will add them to the kit:
153
+
154
+ gjp mvn dependency:sources
155
+
156
+ Unfortunately this will not take care of Maven itself, Maven's plugins and their dependencies so some extra work might be needed.
157
+
158
+ At the moment `gjp`'s supprort to kit source retrieval is limited to the following subcommands:
159
+
35
160
  * `gjp get-pom NAME` will attempt to find a pom. `NAME` can be a jar file on your disk, a project directory, or simply a `name-version` string. `gjp` will get the pom either from the package itself or through search.maven.org using heuristic searching;
36
161
  * `gjp get-parent-pom POM` will attempt to download a pom's parent from search.maven.org, where `POM` is a filename or URI;
37
162
  * `gjp get-source-address POM` will attempt to find the SCM Internet address of a pom.xml from the file itself or through api.github.com. `POM` can either be a filename or a URI;
38
163
  * `gjp get-source POM ADDRESS` downloads the source of a pom.xml's project from its SCM at ADDRESS;
39
- * `gjp scaffold-jar-table DIRECTORY` looks for jars in the project's DIRECTORY and classifies them as build-time dependencies (b), run-time dependencies (r) or products (p);
40
164
 
41
- ## Source
165
+ More comprehensive support is planned in future releases.
166
+
167
+ You are advised to use [Maven Central](http://search.maven.org/) to search for sources and other information about projects.
168
+
169
+ ### Implementation note
170
+
171
+ `gjp` internally uses `git` to keep track of files, any gjp project is actually also a `git` repo. Feel free to navigate it, you can commit, push and pull freely as long as the `gjp` tags are preserved.
172
+
173
+ ## Motivation
174
+
175
+ The Java developer world has packages (jars, wars, ears...), tools ([ant](http://ant.apache.org/), [Maven](http://maven.apache.org/), [Ivy](http://ant.apache.org/ivy/), [Gradle](http://www.gradle.org/)...) and established workflows to handle software distribution while Linux distros have their own ([zypper](http://en.opensuse.org/Portal:Zypper), [yum](http://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified), [apt](http://en.wikipedia.org/wiki/Advanced_Packaging_Tool)...). Since the two communities have different goals and requirements, Linux distros typically want to repackage Java software with their own format, tools and workflows. Reasons range from ease of installation, predictable rebuildability, support to (security) patching, management tools, legal issues, etc.
176
+
177
+ Unfortunately those two "schemes" became very different over time, so automatic translation/repackaging is not possible except in very simple cases. This leads to a lot of tedious, error-prone manual work that Linux packagers have to do in order to fit the "alien" Java model into distro packaging rules that were thought and optimized with a different ecosystem in mind.
178
+
179
+ A typical example is packaging any software built by Maven on SUSE distros. A number of pain points arise:
180
+
181
+ * Maven requires Internet access and downloads precompiled code as part of the build. RPMs have to be built on a standalone machine in [OBS](http://en.opensuse.org/openSUSE:Build_Service), and they should build code from sources they are provided beforehands exclusively;
182
+ * [Maven is basically a plugin container](http://maven.apache.org/plugins/), so hundreds of different plugins have to be installed to build real-life projects (the exact plugin set and their dependencies is determined at build time). While this is no big deal for Java developers, since they get the corresponding jars prebuilt from Maven itself, it is a nightmare for distros, because all shipping code is supposed to be built from scratch and packaged!
183
+ * Maven often uses multiple versions of a same library or plugin during the same build. Usually distros do not maintain more than one version of any given library to reduce maintenance;
184
+ * Maven requires itself in order to build. To be more exact, Maven needs Nexus, which in turn needs Maven and Nexus. To be more exact, its build dependency graph is a very complicated mess with lots of cycles that have to be broken manually.
185
+
186
+ The current solution in openSUSE is having the packager handle those differences, but this limits the amount of software the community is able to package due to the high effort required to overcome them.
187
+
188
+ The Fedora community is experimenting with another set of tools, [XMvn](http://mizdebsk.fedorapeople.org/xmvn/site/), which goals are similar to `gjp`'s.
189
+
190
+ ### Kit rationale
191
+
192
+ `gjp` simplifies the packaging process mostly because of its use of a binary blob package that contains all build time dependencies for a set of packages called a **kit**.
193
+
194
+ Building software from a binary blob is unusual for Linux distros, and it surely has some drawbacks. It is anyway believed that benefits outweigh them, in fact using prebuilt software:
195
+
196
+ * drastically reduces packaging efforts. A very basic and relatively simple package like [commons-lang](http://commons.apache.org/proper/commons-lang/) needs about [150 jars](https://build.opensuse.org/package/show/home:SilvioMoioli/galaxy-kit) just to be compiled and tested. Those should be packaged, roughly, one-by-one!
197
+ * is just the way all Java developers out there build, test and use their software — this is how they expect it to work. Any different approach is necessarily error-prone and could result in unexpected bugs;
198
+ * does not affect the ability of providing patches to Java projects, as only build time requirements are in the kit. In virtually all cases patching a piece of software does not require to patch its build toolchain;
199
+ * does not affect the ability of complying to software licenses like the GPL. In fact those licenses only require to redistribute a project's source code - not the whole toolchain needed to build it. [Sources can be added](#kit-sources) for GPL'd parts of the kit, if any.
200
+
201
+ ## Sources
42
202
 
43
203
  `gjp`'s Git repo is available on GitHub, which can be browsed at:
44
204
 
@@ -20,16 +20,19 @@ module Gjp
20
20
  def initialize(project, package_name, pom, filter)
21
21
  @name = package_name
22
22
  @version = pom.version
23
- @license = pom.license_name
24
- clean_description = pom.description.gsub(/[\s]+/, ' ').strip
25
- @summary = clean_description[0..60].gsub(/\s\w+$/, '...')
23
+ @license = if pom.license_name != ""
24
+ pom.license_name
25
+ else
26
+ "Apache-2.0"
27
+ end
28
+ @summary = cleanup_description(pom.description, 60)
26
29
  @url = pom.url
27
30
  @project_name = project.name
28
31
  @group_id = pom.group_id
29
32
  @artifact_id = pom.artifact_id
30
33
  @version = pom.version
31
34
  @runtime_dependency_ids = pom.runtime_dependency_ids
32
- @description = clean_description
35
+ @description = cleanup_description(pom.description, 1500)
33
36
 
34
37
  output_list = File.join(project.full_path, "file_lists", "#{@name}_output")
35
38
  @outputs = File.open(output_list).readlines.map do |line|
@@ -42,6 +45,15 @@ module Gjp
42
45
  def get_binding
43
46
  binding
44
47
  end
48
+
49
+ def cleanup_description(raw, max_length)
50
+ raw
51
+ .gsub(/[\s]+/, " ")
52
+ .strip
53
+ .slice(0..max_length -1)
54
+ .sub(/\s\w+$/, "")
55
+ .sub(/\.+$/, "")
56
+ end
45
57
  end
46
58
  end
47
59
 
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Gjp
4
- VERSION = "0.15.7"
4
+ VERSION = "0.16.0"
5
5
  end
@@ -24,8 +24,8 @@ Url: https://github.com/SilvioMoioli/gjp
24
24
  Group: Development/Libraries/Java
25
25
  Source0: %{name}.tar.xz
26
26
  BuildRoot: %{_tmppath}/%{name}-%{version}-build
27
- BuildRequires: java-devel
28
27
  BuildArch: noarch
28
+ BuildRequires: xz
29
29
  Provides: gjp(kit)
30
30
  # no two kits should ever be installed at any given time
31
31
  Conflicts: otherproviders(gjp(kit))
@@ -50,7 +50,6 @@ cp -a * %{buildroot}%{_datadir}/gjp/%{name}/
50
50
  %files
51
51
  %defattr(-,root,root)
52
52
  %doc ../README.SUSE
53
- %{_datadir}/gjp
54
- %{_datadir}/gjp/%{name}/
53
+ %{_datadir}/gjp/
55
54
 
56
55
  %changelog
@@ -24,6 +24,8 @@ Url: <%= url %>
24
24
  Group: Development/Libraries/Java
25
25
  Source0: %{name}.tar.xz
26
26
  BuildRoot: %{_tmppath}/%{name}-%{version}-build
27
+ BuildRequires: xz
28
+ BuildRequires: java-devel
27
29
  BuildRequires: <%= project_name %>-kit
28
30
  BuildArch: noarch
29
31
  Provides: mvn(<%= group_id %>:<%= artifact_id %>) == <%= version %>
@@ -32,15 +34,20 @@ Requires: mvn(<%= dependency_id[0] %>:<%= dependency_id[1] %>) <% if depen
32
34
  <% end %>
33
35
 
34
36
  %description
35
- <%= description %>
37
+ <%=
38
+ description
39
+ %>
36
40
 
37
41
  %prep
38
- %setup -q -c
42
+ %setup -q -c -n src/<%= name %>
43
+ ln -sf %{_datadir}/gjp/<%= project_name %>-kit ../../kit
39
44
 
40
45
  %build
41
- ./build.sh
46
+ cd ../../
47
+ sh src/<%= name %>/build.sh
42
48
 
43
49
  %install
50
+ mkdir -p %{buildroot}%{_javadir}
44
51
  <% outputs.each do |output| %>
45
52
  cp -a <%= output %> %{buildroot}%{_javadir}/<%= File.basename(output) %>
46
53
  <% end %>
@@ -48,7 +55,7 @@ cp -a <%= output %> %{buildroot}%{_javadir}/<%= File.basename(output) %>
48
55
  %files
49
56
  %defattr(-,root,root)
50
57
  <% outputs.each do |output| %>
51
- cp -a <%= output %> %{buildroot}%{_javadir}/<%= File.basename(output) %>
58
+ %{_javadir}/<%= File.basename(output) %>
52
59
  <% end %>
53
60
 
54
61
  %changelog
@@ -115,7 +115,7 @@ describe Gjp::Scaffolder do
115
115
  spec_lines = File.readlines(File.join("specs", "test.spec"))
116
116
  spec_lines.should include("Name: test\n")
117
117
  spec_lines.should include("License: The Apache Software License, Version 2.0\n")
118
- spec_lines.should include("Summary: Nailgun is a client, protocol, and server for running Java...\n")
118
+ spec_lines.should include("Summary: Nailgun is a client, protocol, and server for running Java\n")
119
119
  spec_lines.should include("Url: http://martiansoftware.com/nailgun\n")
120
120
  spec_lines.should include("BuildRequires: #{@project.name}-kit\n")
121
121
  spec_lines.should include("Provides: mvn(com.martiansoftware:nailgun-all) == 0.9.1\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gjp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.7
4
+ version: 0.16.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-03 00:00:00.000000000 Z
12
+ date: 2013-10-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake