]> git.argeo.org Git - gpl/argeo-slc.git/commitdiff
Introduce RPM factory
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 11 Aug 2010 14:13:29 +0000 (14:13 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 11 Aug 2010 14:13:29 +0000 (14:13 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@3762 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/CreateSrpm.java [new file with mode: 0644]
runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/ImportSrpm.java [new file with mode: 0644]
runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmBuildEnvironment.java [new file with mode: 0644]
runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmSpecFile.java [new file with mode: 0644]

diff --git a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/CreateSrpm.java b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/CreateSrpm.java
new file mode 100644 (file)
index 0000000..e48df91
--- /dev/null
@@ -0,0 +1,178 @@
+package org.argeo.slc.lib.linux.rpmfactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.core.execution.tasks.SystemCall;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+
+/** Generates an SRPM from a spec file */
+public class CreateSrpm implements Runnable {
+       private final static Log log = LogFactory.getLog(CreateSrpm.class);
+
+       private File topdir;
+
+       private Resource specFile;
+
+       private RpmBuildEnvironment rpmBuildEnvironment;
+
+       private Boolean overwriteSources = false;
+
+       private File srpmFile;
+
+       public void run() {
+               File sourcesDir = new File(topdir, "SOURCES");
+               sourcesDir.mkdirs();
+               File specsDir = new File(topdir, "SPECS");
+               File srpmsDir = new File(topdir, "SRPMS");
+
+               try {
+                       // Parse spec file and copy required resources
+                       RpmSpecFile spec = new RpmSpecFile(specFile);
+                       copyToSources(spec, sourcesDir);
+
+                       // Copy spec file
+                       File targetFile = new File(specsDir, specFile.getFilename())
+                                       .getCanonicalFile();
+                       copyResourceToFile(specFile, targetFile);
+
+                       // Generate rpmbuild config files
+                       File rpmmacroFile = new File(topdir, "rpmmacros");
+                       File rpmrcFile = new File(topdir, "rpmrc");
+                       rpmBuildEnvironment.writeRpmbuildConfigFiles(topdir, rpmmacroFile,
+                                       rpmrcFile);
+
+                       // Build SRPM
+                       srpmsDir.mkdirs();
+                       SystemCall packageSrpm = new SystemCall();
+                       packageSrpm.arg("rpmbuild");
+                       packageSrpm.arg("-bs").arg("--nodeps");
+                       packageSrpm.arg("--rcfile=" + rpmrcFile.getName());
+                       // buildSrpm.arg("-D", "_topdir " + topdir.getCanonicalPath() + "");
+                       packageSrpm.arg("SPECS/" + specFile.getFilename());
+                       packageSrpm.setExecDir(topdir.getCanonicalPath());
+                       packageSrpm.setLogCommand(true);
+
+                       // Execute
+                       String answer = packageSrpm.function();
+
+                       // Extract generated SRPM path
+                       // TODO: make it safer
+                       String srpmPath = answer.split(":")[1].trim();
+                       srpmFile = new File(srpmPath);
+               } catch (IOException e) {
+                       throw new SlcException("Cannot generate SRPM from " + specFile, e);
+               }
+
+       }
+
+       protected void copyToSources(RpmSpecFile spec, File sourcesDir) {
+               try {
+                       List<Resource> toCopyToSources = new ArrayList<Resource>();
+                       for (String file : spec.getSources().values()) {
+                               try {
+                                       Resource res;
+                                       try {
+                                               res = specFile.createRelative("../SOURCES/" + file);
+                                               if (!res.exists())
+                                                       res = new UrlResource(file);
+
+                                       } catch (Exception e) {
+                                               res = new UrlResource(file);
+                                       }
+                                       toCopyToSources.add(res);
+                               } catch (Exception e) {
+                                       log.error("Cannot interpret " + file, e);
+                               }
+                       }
+                       for (String file : spec.getPatches().values()) {
+                               try {
+                                       Resource res;
+                                       try {
+                                               res = specFile.createRelative("../SOURCES/" + file);
+                                               if (!res.exists()) {
+                                                       res = new UrlResource(file);
+                                               }
+                                       } catch (Exception e) {
+                                               res = new UrlResource(file);
+                                       }
+                                       toCopyToSources.add(res);
+                               } catch (Exception e) {
+                                       log.error("Cannot interpret " + file, e);
+                               }
+                       }
+
+                       // FIXME: we may have missed some files here
+                       copySources: for (Resource res : toCopyToSources) {
+                               File targetFile = new File(sourcesDir, res.getFilename())
+                                               .getCanonicalFile();
+                               if (targetFile.exists() && !overwriteSources)
+                                       continue copySources;
+                               copyResourceToFile(res, targetFile);
+                       }
+               } catch (Exception e) {
+                       throw new SlcException("Cannot copy to " + sourcesDir, e);
+               }
+       }
+
+       private static void copyResourceToFile(Resource res, File targetFile) {
+               try {
+                       if (targetFile.equals(res.getFile())) {
+                               if (log.isDebugEnabled())
+                                       log.debug("Target identical to source, skipping... "
+                                                       + targetFile + " <=> " + res);
+                               return;
+                       }
+               } catch (IOException e1) {
+                       // silent
+               }
+
+               OutputStream out = null;
+               InputStream in = null;
+               try {
+                       out = FileUtils.openOutputStream(targetFile);
+                       in = res.getInputStream();
+                       IOUtils.copy(in, out);
+                       if (log.isDebugEnabled())
+                               log.debug("Copied " + targetFile + " from " + res);
+               } catch (Exception e) {
+                       throw new SlcException("Cannot copy " + res + " to " + targetFile,
+                                       e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       IOUtils.closeQuietly(out);
+               }
+
+       }
+
+       public void setSpecFile(Resource specFile) {
+               this.specFile = specFile;
+       }
+
+       public void setTopdir(File topdir) {
+               this.topdir = topdir;
+       }
+
+       public void setOverwriteSources(Boolean overwriteSources) {
+               this.overwriteSources = overwriteSources;
+       }
+
+       public File getSrpmFile() {
+               return srpmFile;
+       }
+
+       public void setRpmBuildEnvironment(RpmBuildEnvironment rpmBuildEnvironment) {
+               this.rpmBuildEnvironment = rpmBuildEnvironment;
+       }
+
+}
diff --git a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/ImportSrpm.java b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/ImportSrpm.java
new file mode 100644 (file)
index 0000000..e005608
--- /dev/null
@@ -0,0 +1,62 @@
+package org.argeo.slc.lib.linux.rpmfactory;
+
+import java.io.File;
+
+import org.argeo.slc.core.execution.tasks.SystemCall;
+
+/** Install an SRPM into a working copy */
+public class ImportSrpm implements Runnable {
+       private File baseDir;
+       private File srpmFile;
+       private RpmBuildEnvironment rpmBuildEnvironment;
+
+       public void run() {
+               SystemCall rpmQuery = new SystemCall(
+                               "rpm --queryformat '%{NAME}\n' -qp " + srpmFile);
+               String packageName = rpmQuery.function();
+
+               File topdir = new File(baseDir, packageName);
+
+               // prepare SVN
+               // TODO: do it with SVNKit
+               topdir.mkdirs();
+               new SystemCall("svn add " + topdir).run();
+               new SystemCall("svn propset svn:ignore rpm*\nBUILD\nSRPMS\nRPMS " + topdir).run();
+               File sourcesDir = new File(topdir, "SOURCES");
+               sourcesDir.mkdirs();
+               new SystemCall("svn add " + sourcesDir).run();
+               new SystemCall("svn propset svn:ignore *gz\n*bz2\n*.zip\n*.jar " + sourcesDir).run();
+               File specsDir = new File(topdir, "SPECS");
+               specsDir.mkdirs();
+               new SystemCall("svn add " + specsDir).run();
+
+               // Write rpm config files
+               File rpmmacroFile = new File(topdir, "rpmmacros");
+               File rpmrcFile = new File(topdir, "rpmrc");
+               rpmBuildEnvironment.writeRpmbuildConfigFiles(topdir, rpmmacroFile,
+                               rpmrcFile);
+
+               // Install SRPM
+               SystemCall installSrpm = new SystemCall();
+               installSrpm.arg("rpm");
+               installSrpm.arg("-Uvh");
+               installSrpm.arg("--rcfile=" + rpmrcFile.getAbsolutePath());
+               installSrpm.arg(srpmFile.getAbsolutePath());
+               installSrpm.setExecDir(topdir.getAbsolutePath());
+               installSrpm.setLogCommand(true);
+               installSrpm.run();
+       }
+
+       public void setBaseDir(File basedir) {
+               this.baseDir = basedir;
+       }
+
+       public void setSrpmFile(File srpmFile) {
+               this.srpmFile = srpmFile;
+       }
+
+       public void setRpmBuildEnvironment(RpmBuildEnvironment rpmBuildEnvironment) {
+               this.rpmBuildEnvironment = rpmBuildEnvironment;
+       }
+
+}
diff --git a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmBuildEnvironment.java b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmBuildEnvironment.java
new file mode 100644 (file)
index 0000000..a3b0d95
--- /dev/null
@@ -0,0 +1,55 @@
+package org.argeo.slc.lib.linux.rpmfactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.argeo.slc.SlcException;
+
+public class RpmBuildEnvironment {
+       private String defaultMacroFiles = "/usr/lib/rpm/macros:/usr/lib/rpm/ia32e-linux/macros:/usr/lib/rpm/redhat/macros:/etc/rpm/macros.*:/etc/rpm/macros:/etc/rpm/ia32e-linux/macros:~/.rpmmacros";
+       private Map<String, String> rpmmacros = new HashMap<String, String>();
+
+       public void writeRpmbuildConfigFiles(File topdir, File rpmmacroFile,
+                       File rpmrcFile) {
+               try {
+                       List<String> macroLines = new ArrayList<String>();
+                       macroLines.add("%_topdir " + topdir.getCanonicalPath());
+                       for (String macroKey : rpmmacros.keySet()) {
+                               macroLines.add(macroKey + " " + rpmmacros.get(macroKey));
+                       }
+                       FileUtils.writeLines(rpmmacroFile, macroLines);
+
+                       List<String> rpmrcLines = new ArrayList<String>();
+                       rpmrcLines.add("include: /usr/lib/rpm/rpmrc");
+                       rpmrcLines.add("macrofiles: " + defaultMacroFiles + ":"
+                                       + rpmmacroFile.getCanonicalPath());
+                       FileUtils.writeLines(rpmrcFile, rpmrcLines);
+               } catch (IOException e) {
+                       throw new SlcException("Cannot write rpmbuild config files",
+                                       e);
+               }
+
+       }
+
+       public Map<String, String> getRpmmacros() {
+               return rpmmacros;
+       }
+
+       public void setRpmmacros(Map<String, String> rpmmacros) {
+               this.rpmmacros = rpmmacros;
+       }
+
+       public String getDefaultMacroFiles() {
+               return defaultMacroFiles;
+       }
+
+       public void setDefaultMacroFiles(String defaultMacroFiles) {
+               this.defaultMacroFiles = defaultMacroFiles;
+       }
+
+}
diff --git a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmSpecFile.java b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmSpecFile.java
new file mode 100644 (file)
index 0000000..899603a
--- /dev/null
@@ -0,0 +1,113 @@
+package org.argeo.slc.lib.linux.rpmfactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.springframework.core.io.Resource;
+
+public class RpmSpecFile {
+       private Resource specFile;
+
+       private String name;
+       private String version;
+       private String release;
+       private Map<String, String> sources = new HashMap<String, String>();
+       private Map<String, String> patches = new HashMap<String, String>();
+
+       public RpmSpecFile(Resource specFile) {
+               this.specFile = specFile;
+               parseSpecFile();
+       }
+
+       public void init() {
+               parseSpecFile();
+       }
+
+       @SuppressWarnings("unchecked")
+       protected void parseSpecFile() {
+               try {
+                       List<String> lines = (List<String>) IOUtils.readLines(specFile
+                                       .getInputStream());
+
+                       lines: for (String line : lines) {
+                               int indexSemiColon = line.indexOf(':');
+                               if (indexSemiColon <= 0)
+                                       continue lines;
+                               String directive = line.substring(0, indexSemiColon).trim();
+                               String value = line.substring(indexSemiColon + 1).trim();
+                               if ("name".equals(directive.toLowerCase()))
+                                       name = value;
+                               else if ("version".equals(directive.toLowerCase()))
+                                       version = value;
+                               else if ("release".equals(directive.toLowerCase()))
+                                       release = value;
+                               else if (directive.toLowerCase().startsWith("source"))
+                                       sources.put(directive, interpret(value));
+                               else if (directive.toLowerCase().startsWith("patch"))
+                                       patches.put(directive, interpret(value));
+                       }
+
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot parse spec file " + specFile, e);
+               }
+       }
+
+       protected String interpret(String value) {
+               StringBuffer buf = new StringBuffer(value.length());
+               StringBuffer currKey = null;
+               boolean mayBeKey = false;
+               chars: for (char c : value.toCharArray()) {
+                       if (c == '%')
+                               mayBeKey = true;
+                       else if (c == '{') {
+                               if (mayBeKey)
+                                       currKey = new StringBuffer();
+                       } else if (c == '}') {
+                               if (currKey == null)
+                                       continue chars;
+                               String key = currKey.toString();
+                               if ("name".equals(key.toLowerCase()))
+                                       buf.append(name);
+                               else if ("version".equals(key.toLowerCase()))
+                                       buf.append(version);
+                               else
+                                       buf.append("%{").append(key).append('}');
+                               currKey = null;
+                       } else {
+                               if (currKey != null)
+                                       currKey.append(c);
+                               else
+                                       buf.append(c);
+                       }
+               }
+               return buf.toString();
+       }
+
+       public Resource getSpecFile() {
+               return specFile;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public String getVersion() {
+               return version;
+       }
+
+       public String getRelease() {
+               return release;
+       }
+
+       public Map<String, String> getSources() {
+               return sources;
+       }
+
+       public Map<String, String> getPatches() {
+               return patches;
+       }
+
+}