From 8f889799e6a47e28225dd0c9e8f9502f35adb42f Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 11 Aug 2010 14:13:29 +0000 Subject: [PATCH 1/1] Introduce RPM factory git-svn-id: https://svn.argeo.org/slc/trunk@3762 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../slc/lib/linux/rpmfactory/CreateSrpm.java | 178 ++++++++++++++++++ .../slc/lib/linux/rpmfactory/ImportSrpm.java | 62 ++++++ .../linux/rpmfactory/RpmBuildEnvironment.java | 55 ++++++ .../slc/lib/linux/rpmfactory/RpmSpecFile.java | 113 +++++++++++ 4 files changed, 408 insertions(+) create mode 100644 runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/CreateSrpm.java create mode 100644 runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/ImportSrpm.java create mode 100644 runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmBuildEnvironment.java create mode 100644 runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmSpecFile.java 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 index 000000000..e48df9143 --- /dev/null +++ b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/CreateSrpm.java @@ -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 toCopyToSources = new ArrayList(); + 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 index 000000000..e00560832 --- /dev/null +++ b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/ImportSrpm.java @@ -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 index 000000000..a3b0d95c4 --- /dev/null +++ b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmBuildEnvironment.java @@ -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 rpmmacros = new HashMap(); + + public void writeRpmbuildConfigFiles(File topdir, File rpmmacroFile, + File rpmrcFile) { + try { + List macroLines = new ArrayList(); + macroLines.add("%_topdir " + topdir.getCanonicalPath()); + for (String macroKey : rpmmacros.keySet()) { + macroLines.add(macroKey + " " + rpmmacros.get(macroKey)); + } + FileUtils.writeLines(rpmmacroFile, macroLines); + + List rpmrcLines = new ArrayList(); + 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 getRpmmacros() { + return rpmmacros; + } + + public void setRpmmacros(Map 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 index 000000000..899603a7b --- /dev/null +++ b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/linux/rpmfactory/RpmSpecFile.java @@ -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 sources = new HashMap(); + private Map patches = new HashMap(); + + public RpmSpecFile(Resource specFile) { + this.specFile = specFile; + parseSpecFile(); + } + + public void init() { + parseSpecFile(); + } + + @SuppressWarnings("unchecked") + protected void parseSpecFile() { + try { + List lines = (List) 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 getSources() { + return sources; + } + + public Map getPatches() { + return patches; + } + +} -- 2.39.2