You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by do...@apache.org on 2014/05/11 03:10:50 UTC

[3/3] git commit: Import 'buildr/custom_pom' addon to make it easier to build POMs for projects publishing to Maven Central.

Import 'buildr/custom_pom' addon to make it easier to build POMs for projects publishing to Maven Central.


Project: http://git-wip-us.apache.org/repos/asf/buildr/repo
Commit: http://git-wip-us.apache.org/repos/asf/buildr/commit/baa269bb
Tree: http://git-wip-us.apache.org/repos/asf/buildr/tree/baa269bb
Diff: http://git-wip-us.apache.org/repos/asf/buildr/diff/baa269bb

Branch: refs/heads/master
Commit: baa269bb3243a7615e05bb95f6c799f112adafec
Parents: 54a832d
Author: Peter Donald <pe...@realityforge.org>
Authored: Sun May 11 11:10:35 2014 +1000
Committer: Peter Donald <pe...@realityforge.org>
Committed: Sun May 11 11:10:35 2014 +1000

----------------------------------------------------------------------
 CHANGELOG                     |   2 +
 addon/buildr/custom_pom.rb    | 283 +++++++++++++++++++++++++++++++++++++
 doc/more_stuff.textile        |  72 ++++++++++
 spec/addon/custom_pom_spec.rb | 149 +++++++++++++++++++
 4 files changed, 506 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/buildr/blob/baa269bb/CHANGELOG
----------------------------------------------------------------------
diff --git a/CHANGELOG b/CHANGELOG
index 3f4a4ce..4aaae82 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,6 @@
 1.4.17 (Pending)
+* Added:  Import 'buildr/custom_pom' addon to make it easier to
+          build POMs for projects publishing to Maven Central.
 * Added:  Add flag to allow non portable extensions in wsgen addon.
 * Fixed:  Avoid nil dereference bug in GWT addon when running GWT in
           a project that has no source directory.

http://git-wip-us.apache.org/repos/asf/buildr/blob/baa269bb/addon/buildr/custom_pom.rb
----------------------------------------------------------------------
diff --git a/addon/buildr/custom_pom.rb b/addon/buildr/custom_pom.rb
new file mode 100644
index 0000000..76b51d8
--- /dev/null
+++ b/addon/buildr/custom_pom.rb
@@ -0,0 +1,283 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with this
+# work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+module Buildr
+  class CustomPom
+    Developer = Struct.new(:id, :name, :email, :roles)
+
+    # Specify the name of the project
+    attr_writer :name
+
+    # Retrieve the name of the project, defaulting to the project description or the name if not specified
+    def name
+      @name || @buildr_project.comment || @buildr_project.name
+    end
+
+    # Specify a project description
+    attr_writer :description
+
+    # Retrieve the project description, defaulting to the name if not specified
+    def description
+      @description || name
+    end
+
+    # Property for the projects url
+    attr_accessor :url
+
+    # Return the map of licenses for project
+    def licenses
+      @licenses ||= {}
+    end
+
+    # Add Apache2 to the list of licenses
+    def add_apache_v2_license
+      self.licenses['The Apache Software License, Version 2.0'] = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+    end
+
+    def add_bsd_2_license
+      self.licenses['The BSD 2-Clause License'] = 'http://opensource.org/licenses/BSD-2-Clause'
+    end
+
+    def add_bsd_3_license
+      self.licenses['The BSD 3-Clause License'] = 'http://opensource.org/licenses/BSD-3-Clause'
+    end
+
+    def add_cddl_v1_license
+      self.licenses['Common Development and Distribution License (CDDL-1.0)'] = 'http://opensource.org/licenses/CDDL-1.0'
+    end
+
+    def add_epl_v1_license
+      self.licenses['Eclipse Public License - v 1.0'] = 'http://www.eclipse.org/legal/epl-v10.html'
+    end
+
+    def add_gpl_v1_license
+      self.licenses['GNU General Public License (GPL) version 1.0'] = 'http://www.gnu.org/licenses/gpl-1.0.html'
+    end
+
+    def add_gpl_v2_license
+      self.licenses['GNU General Public License (GPL) version 2.0'] = 'http://www.gnu.org/licenses/gpl-2.0.html'
+    end
+
+    def add_gpl_v3_license
+      self.licenses['GNU General Public License (GPL) version 3.0'] = 'http://www.gnu.org/licenses/gpl-3.0.html'
+    end
+
+    def add_lgpl_v2_license
+      self.licenses['GNU General Lesser Public License (LGPL) version 2.1'] = 'http://www.gnu.org/licenses/lgpl-2.1.html'
+    end
+
+    def add_lgpl_v3_license
+      self.licenses['GNU General Lesser Public License (LGPL) version 3.0'] = 'http://www.gnu.org/licenses/lgpl-3.0.html'
+    end
+
+    def add_mit_license
+      self.licenses['The MIT License'] = 'http://opensource.org/licenses/MIT'
+    end
+
+
+    attr_accessor :scm_url
+    attr_accessor :scm_connection
+    attr_accessor :scm_developer_connection
+
+    attr_accessor :issues_url
+    attr_accessor :issues_system
+
+    # Add a project like add_github_project('realityforge/gwt-appcache')
+    def add_github_project(project_spec)
+      git_url = "git@github.com:#{project_spec}.git"
+      self.scm_connection = self.scm_developer_connection = "scm:git:#{git_url}"
+      self.scm_url = git_url
+      web_url = "https://github.com/#{project_spec}"
+      self.url = web_url
+      self.issues_url = "#{web_url}/issues"
+      self.issues_system = 'GitHub Issues'
+    end
+
+    def developers
+      @developers ||= []
+    end
+
+    def add_developer(id, name = nil, email = nil, roles = nil)
+      self.developers << Developer.new(id, name, email, roles)
+    end
+
+    def provided_dependencies
+      @provided_dependencies ||= []
+    end
+
+    def provided_dependencies=(provided_dependencies)
+      @provided_dependencies = provided_dependencies
+    end
+
+    def runtime_dependencies
+      @runtime_dependencies ||= []
+    end
+
+    def runtime_dependencies=(runtime_dependencies)
+      @runtime_dependencies = runtime_dependencies
+    end
+
+    def optional_dependencies
+      @optional_dependencies ||= []
+    end
+
+    def optional_dependencies=(optional_dependencies)
+      @optional_dependencies = optional_dependencies
+    end
+
+    protected
+
+    def associate_project(buildr_project)
+      @buildr_project = buildr_project
+    end
+
+    def self.pom_xml(project, package)
+      Proc.new do
+        xml = Builder::XmlMarkup.new(:indent => 2)
+        xml.instruct!
+        xml.project('xmlns' => 'http://maven.apache.org/POM/4.0.0',
+                    'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+                    'xsi:schemaLocation' => 'http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd') do
+          xml.modelVersion '4.0.0'
+          xml.parent do
+            xml.groupId 'org.sonatype.oss'
+            xml.artifactId 'oss-parent'
+            xml.version '7'
+          end
+          xml.groupId project.group
+          xml.artifactId project.id
+          xml.version project.version
+          xml.packaging package.type.to_s
+          xml.classifier package.classifier if package.classifier
+
+          xml.name project.pom.name if project.pom.name
+          xml.description project.pom.description if project.pom.description
+          xml.url project.pom.url if project.pom.url
+
+          xml.licenses do
+            project.pom.licenses.each_pair do |name, url|
+              xml.license do
+                xml.name name
+                xml.url url
+                xml.distribution 'repo'
+              end
+            end
+          end
+
+          if project.pom.scm_url || project.pom.scm_connection || project.pom.scm_developer_connection
+            xml.scm do
+              xml.connection project.pom.scm_connection if project.pom.scm_connection
+              xml.developerConnection project.pom.scm_developer_connection if project.pom.scm_developer_connection
+              xml.url project.pom.scm_url if project.pom.scm_url
+            end
+          end
+
+          if project.pom.issues_url
+            xml.issueManagement do
+              xml.url project.pom.issues_url
+              xml.system project.pom.issues_system if project.pom.issues_system
+            end
+          end
+
+          xml.developers do
+            project.pom.developers.each do |developer|
+              xml.developer do
+                xml.id developer.id
+                xml.name developer.name if developer.name
+                xml.email developer.email if developer.email
+                if developer.roles
+                  xml.roles do
+                    developer.roles.each do |role|
+                      xml.role role
+                    end
+                  end
+                end
+              end
+            end
+          end
+
+          xml.dependencies do
+            provided_deps = Buildr.artifacts(project.pom.provided_dependencies).collect { |d| d.to_s }
+            runtime_deps = Buildr.artifacts(project.pom.runtime_dependencies).collect { |d| d.to_s }
+            optional_deps = Buildr.artifacts(project.pom.optional_dependencies).collect { |d| d.to_s }
+            deps =
+              Buildr.artifacts(project.compile.dependencies).
+                select { |d| d.is_a?(Artifact) }.
+                collect do |d|
+                f = d.to_s
+                scope = provided_deps.include?(f) ? 'provided' :
+                  runtime_deps.include?(f) ? 'runtime' :
+                    'compile'
+                d.to_hash.merge(:scope => scope, :optional => optional_deps.include?(f))
+              end + Buildr.artifacts(project.test.compile.dependencies).
+                select { |d| d.is_a?(Artifact) && !project.compile.dependencies.include?(d) }.collect { |d| d.to_hash.merge(:scope => 'test') }
+            deps.each do |dependency|
+              xml.dependency do
+                xml.groupId dependency[:group]
+                xml.artifactId dependency[:id]
+                xml.version dependency[:version]
+                xml.scope dependency[:scope] unless dependency[:scope] == 'compile'
+                xml.optional true if dependency[:optional]
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
+
+module Buildr
+  class Project #:nodoc:
+    def pom
+      unless @pom
+        @pom = parent ? parent.pom.dup : Buildr::CustomPom.new
+        @pom.send :associate_project, self
+      end
+      @pom
+    end
+  end
+end
+
+module Buildr
+  module Package
+    alias :old_package :package
+
+    def package(*args)
+      package = old_package(*args)
+      class << package
+        def pom
+          unless @pom || classifier
+            pom_filename = Util.replace_extension(name, 'pom')
+            spec = {:group => group, :id => id, :version => version, :type => :pom}
+            @pom = Buildr.artifact(spec, pom_filename)
+            buildr_project = Buildr.project(self.scope.join(':'))
+            @pom.content Buildr::CustomPom.pom_xml(buildr_project, self)
+          end
+          @pom
+        end
+      end
+      package.instance_variable_set('@pom', nil)
+      package.enhance([package.pom.to_s]) if package.type.to_s == 'jar' && !package.classifier
+      package
+    end
+  end
+
+  module ActsAsArtifact
+    def pom_xml
+      self.pom.content
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/buildr/blob/baa269bb/doc/more_stuff.textile
----------------------------------------------------------------------
diff --git a/doc/more_stuff.textile b/doc/more_stuff.textile
index 1fdeebe..057ddcd 100644
--- a/doc/more_stuff.textile
+++ b/doc/more_stuff.textile
@@ -391,6 +391,78 @@ $ buildr --generate /path/to/my_project
 
 This creates a basic buildfile with a main project called 'my_project'. The buildfile contains a skeleton for compiling the Eclipse projects. If you want to automate dependency tracking via OSGi have a look at the "buildr4osgi":http://oss.intalio.com/buildr4osgi/ project. Support for building Eclipse RCP applications, running PDE tests and P2-sites is currently lacking in Buildr.
 
+h2(#maven_central). Releasing to Maven Central
+
+Many opensource projects release their artifacts to Maven Central. To release a library to Maven Central, the project needs to provide several elements for each library;
+
+* the jar artifact,
+* the sources artifact,
+* the javadocs artifact,
+* a pom that supplies fields required by Maven Central, and
+* gpg signatures for every file supplied.
+
+Buildr has built-in support for the artifacts and can easily sign the artifacts using the 'buildr/gpg' addon. However it has not always been easy to generate a pom in the required format until the 'buildr/custom_pom' became available.
+
+Below is an extremely verbose example of a project that provides all the elements required to publish to Maven Central.
+
+{% highlight sh %}
+# Include addon to generate GPG Signatures
+require 'buildr/gpg'
+# Include addon to generate custom pom
+require 'buildr/custom_pom'
+
+define 'myproject' do
+  project.group = 'org.myproject'
+  project.version = '1.0'
+
+  pom.licenses['The Apache Software License, Version 2.0'] = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+  pom.scm_connection = pom.scm_developer_connection = 'scm:git:git@github.com:jbloggs/myproject'
+  pom.scm_url = 'git@github.com:jbloggs/myproject'
+  pom.url = 'https://github.com/jbloggs/myproject'
+  pom.issues_url = 'https://github.com/jbloggs/myproject/issues'
+  pom.issues_system = 'GitHub Issues'
+  pom.add_developer('jbloggs', 'Joe Bloggs', 'jbloggs@example.com', ['Project Lead'])
+  pom.provided_dependencies.concat [:javax_servlet]
+  pom.optional_dependencies.concat [:optional_api]
+
+  compile.with :javax_servlet, :some_api, :optional_api
+
+  test.with :mockito
+
+  package(:jar)
+  package(:sources)
+  package(:javadoc)
+end
+{% endhighlight %}
+
+That example is however, extremely verbose and there is a number of helper methods been added to the 'buildr/custom_pom' addon to simplify common scenarios. It would be more common to see the addon used in the following manner;
+
+{% highlight sh %}
+require 'buildr/gpg'
+require 'buildr/custom_pom'
+
+define 'myproject' do
+  project.group = 'org.myproject'
+  project.version = '1.0'
+
+  pom.add_apache2_license
+  pom.add_github_project('jbloggs/myproject')
+  pom.add_developer('jbloggs', 'Joe Bloggs')
+  pom.provided_dependencies.concat [:javax_servlet]
+  pom.optional_dependencies.concat [:optional_api]
+
+  compile.with :javax_servlet, :optional_api
+
+  test.with :mockito
+
+  package(:jar)
+  package(:sources)
+  package(:javadoc)
+end
+{% endhighlight %}
+
+If there are other common scenarios useful for opensource developers, feel free to make a request on buildr mailing list to provide simplified helper methods.
+
 h2(#idea). IntelliJ IDEA
 
 If you use IntelliJ IDEA, you can generate project files by issuing:

http://git-wip-us.apache.org/repos/asf/buildr/blob/baa269bb/spec/addon/custom_pom_spec.rb
----------------------------------------------------------------------
diff --git a/spec/addon/custom_pom_spec.rb b/spec/addon/custom_pom_spec.rb
new file mode 100644
index 0000000..e201ad1
--- /dev/null
+++ b/spec/addon/custom_pom_spec.rb
@@ -0,0 +1,149 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with this
+# work for additional information regarding copyright ownership. The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+
+require File.expand_path('../spec_helpers', File.dirname(__FILE__))
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'xpath_matchers'))
+
+Sandbox.require_optional_extension 'buildr/custom_pom'
+
+describe Buildr::CustomPom do
+
+  def xml_document(filename)
+    File.should be_exist(filename)
+    REXML::Document.new(File.read(filename))
+  end
+
+  def project_pom_xml(project)
+    xml_document(project.packages[0].pom.to_s)
+  end
+
+  def verify_license(pom_xml, name, url)
+    pom_xml.should match_xpath("/project/licenses/license/url[../name/text() = '#{name}']", url)
+  end
+
+  def dependency_xpath(artifact_id)
+    "/project/dependencies/dependency[artifactId/text() = '#{artifact_id}']"
+  end
+
+  def verify_dependency_group(pom_xml, artifact_id, group)
+    pom_xml.should match_xpath("#{dependency_xpath(artifact_id)}/groupId", group)
+  end
+
+  def verify_dependency_version(pom_xml, artifact_id, version)
+    pom_xml.should match_xpath("#{dependency_xpath(artifact_id)}/version", version)
+  end
+
+  def verify_dependency_scope(pom_xml, artifact_id, scope)
+    pom_xml.should match_xpath("#{dependency_xpath(artifact_id)}/scope", scope)
+  end
+
+  def verify_dependency_optional(pom_xml, artifact_id, optional)
+    pom_xml.should match_xpath("#{dependency_xpath(artifact_id)}/optional", optional)
+  end
+
+  def verify_dependency(pom_xml, artifact_id, group, version, scope, optional)
+    verify_dependency_group(pom_xml, artifact_id, group)
+    verify_dependency_version(pom_xml, artifact_id, version)
+    verify_dependency_scope(pom_xml, artifact_id, scope)
+    verify_dependency_optional(pom_xml, artifact_id, optional)
+  end
+
+  describe "with explicitly specified pom details" do
+    before do
+      ['id-provided', 'id-optional', 'id-runtime', 'id-test'].each do |artifact_id|
+        artifact("group:#{artifact_id}:jar:1.0") do |t|
+          mkdir_p File.dirname(t.to_s)
+          Zip::ZipOutputStream.open t.to_s do |zip|
+            zip.put_next_entry 'empty.txt'
+          end
+        end
+      end
+      write 'src/main/java/Example.java', "public class Example {}"
+
+      @foo = define 'foo' do
+        project.group = 'org.myproject'
+        project.version = '1.0'
+
+        pom.licenses['The Apache Software License, Version 2.0'] = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+        pom.licenses['GNU General Public License (GPL) version 3.0'] = 'http://www.gnu.org/licenses/gpl-3.0.html'
+        pom.scm_connection = pom.scm_developer_connection = 'scm:git:git@github.com:jbloggs/myproject'
+        pom.scm_url = 'git@github.com:jbloggs/myproject'
+        pom.url = 'https://github.com/jbloggs/myproject'
+        pom.issues_url = 'https://github.com/jbloggs/myproject/issues'
+        pom.issues_system = 'GitHub Issues'
+        pom.add_developer('jbloggs', 'Joe Bloggs', 'jbloggs@example.com', ['Project Lead'])
+        pom.provided_dependencies = ['group:id-provided:jar:1.0']
+        pom.optional_dependencies = ['group:id-optional:jar:1.0']
+
+        compile.with 'group:id-runtime:jar:1.0', 'group:id-optional:jar:1.0', 'group:id-provided:jar:1.0'
+
+        test.with 'group:id-test:jar:1.0'
+
+        package(:jar)
+      end
+      task('package').invoke
+      @pom_xml = project_pom_xml(@foo)
+      #$stderr.puts @pom_xml.to_s
+    end
+
+    it "has correct static metadata" do
+      @pom_xml.should match_xpath("/project/modelVersion", '4.0.0')
+      @pom_xml.should match_xpath("/project/parent/groupId", 'org.sonatype.oss')
+      @pom_xml.should match_xpath("/project/parent/artifactId", 'oss-parent')
+      @pom_xml.should match_xpath("/project/parent/version", '7')
+    end
+
+    it "has correct project level metadata" do
+      @pom_xml.should match_xpath("/project/groupId", 'org.myproject')
+      @pom_xml.should match_xpath("/project/artifactId", 'foo')
+      @pom_xml.should match_xpath("/project/version", '1.0')
+      @pom_xml.should match_xpath("/project/packaging", 'jar')
+      @pom_xml.should match_xpath("/project/name", 'foo')
+      @pom_xml.should match_xpath("/project/description", 'foo')
+      @pom_xml.should match_xpath("/project/url", 'https://github.com/jbloggs/myproject')
+    end
+
+    it "has correct scm details" do
+      @pom_xml.should match_xpath("/project/scm/connection", 'scm:git:git@github.com:jbloggs/myproject')
+      @pom_xml.should match_xpath("/project/scm/developerConnection", 'scm:git:git@github.com:jbloggs/myproject')
+      @pom_xml.should match_xpath("/project/scm/url", 'git@github.com:jbloggs/myproject')
+    end
+
+    it "has correct issueManagement details" do
+      @pom_xml.should match_xpath("/project/issueManagement/url", 'https://github.com/jbloggs/myproject/issues')
+      @pom_xml.should match_xpath("/project/issueManagement/system", 'GitHub Issues')
+    end
+
+    it "has correct developers details" do
+      @pom_xml.should match_xpath("/project/developers/developer/id", 'jbloggs')
+      @pom_xml.should match_xpath("/project/developers/developer/name", 'Joe Bloggs')
+      @pom_xml.should match_xpath("/project/developers/developer/email", 'jbloggs@example.com')
+      @pom_xml.should match_xpath("/project/developers/developer/roles/role", 'Project Lead')
+    end
+
+    it "has correct license details" do
+      verify_license(@pom_xml, 'The Apache Software License, Version 2.0', 'http://www.apache.org/licenses/LICENSE-2.0.txt')
+      verify_license(@pom_xml, 'GNU General Public License (GPL) version 3.0', 'http://www.gnu.org/licenses/gpl-3.0.html')
+    end
+
+    it "has correct dependency details" do
+      verify_dependency(@pom_xml, 'id-runtime', 'group', '1.0', nil, nil)
+      verify_dependency(@pom_xml, 'id-optional', 'group', '1.0', nil, 'true')
+      verify_dependency(@pom_xml, 'id-provided', 'group', '1.0', 'provided', nil)
+      verify_dependency(@pom_xml, 'id-test', 'group', '1.0', 'test', nil)
+    end
+  end
+end