--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}