Fix minor spelling mistakes in RPM build logic
[integration/packaging.git] / rpm / build.py
1 #!/usr/bin/env python
2 """Build OpenDaylight's RPMs using YAML build configs and Jinja2 templates."""
3
4 import os
5 import sys
6 import argparse
7 import shutil
8 import subprocess
9 from string import Template
10
11 try:
12     import yaml
13 except ImportError:
14     sys.stderr.write("We recommend using our included Vagrant env.\n")
15     sys.stderr.write("Else, do `pip install -r requirements.txt` in a venv.\n")
16     raise
17
18 import cache.cache as cache
19 import specs.build_specs as build_specs
20
21 # Common paths used in this script
22 # This file is assumed to be in the root of the RPM build logic's dir structure
23 project_root = os.path.dirname(os.path.abspath(__file__))
24 cache_dir = os.path.join(project_root, "cache")
25 specs_dir = os.path.join(project_root, "specs")
26 rpmbuild_dir = os.path.join(os.path.expanduser("~"), "rpmbuild")
27 src_in_dir = os.path.join(rpmbuild_dir, "SOURCES")
28 spec_in_dir = os.path.join(rpmbuild_dir, "SPECS")
29 srpm_out_dir = os.path.join(rpmbuild_dir, "SRPMS")
30 rpm_out_dir = os.path.join(rpmbuild_dir, "RPMS", "noarch")
31
32 # Templates that can be specialized into common artifact names per-build
33 odl_template = Template("distribution-karaf-0.$version_major."
34                         "$version_minor-$codename.tar.gz")
35 specfile_template = Template("opendaylight-$version_major.$version_minor."
36                              "$version_patch-$rpm_release.spec")
37 unitfile_tb_template = Template("opendaylight-$sysd_commit.service.tar.gz")
38 rpm_template = Template("opendaylight-$version_major.$version_minor."
39                         "$version_patch-$rpm_release.$rpm_disttag.noarch.rpm")
40 srpm_template = Template("opendaylight-$version_major.$version_minor."
41                          "$version_patch-$rpm_release.$rpm_disttag.src.rpm")
42
43
44 def build_rpm(build):
45     """Build the RPMs described by the given build description.
46
47     :param build: Description of an RPM build, typically from rpm_vars.yaml
48     :type build: dict
49
50     """
51     # Specialize a series of name templates for the given build
52     odl_tarball = odl_template.substitute(build)
53     odl_rpm = rpm_template.substitute(build)
54     odl_srpm = srpm_template.substitute(build)
55     odl_specfile = specfile_template.substitute(build)
56     unitfile_tarball = unitfile_tb_template.substitute(build)
57
58     # After building strings from the name templates, build their full path
59     odl_tarball_path = os.path.join(cache_dir, odl_tarball)
60     unitfile_tarball_path = os.path.join(cache_dir, unitfile_tarball)
61     specfile_path = os.path.join(specs_dir, odl_specfile)
62     spec_in_path = os.path.join(spec_in_dir, odl_specfile)
63     rpm_out_path = os.path.join(rpm_out_dir, odl_rpm)
64     srpm_out_path = os.path.join(srpm_out_dir, odl_srpm)
65
66     # Call a helper function to cache the artifacts required for each build
67     cache.cache_build(build)
68
69     # Call helper script to build the required RPM .spec files
70     build_specs.build_spec(build)
71
72     # Clean up old rpmbuild dir structure if it exists
73     if os.path.isdir(rpmbuild_dir):
74         shutil.rmtree(rpmbuild_dir)
75
76     # Create rpmbuild dir structure
77     subprocess.call("rpmdev-setuptree")
78
79     # Move unitfile, tarball and specfile to correct rpmbuild dirs
80     shutil.copy(odl_tarball_path, src_in_dir)
81     shutil.copy(unitfile_tarball_path, src_in_dir)
82     shutil.copy(specfile_path, spec_in_dir)
83
84     # Call rpmbuild, build both SRPMs/RPMs
85     subprocess.call(["rpmbuild", "-ba", spec_in_path])
86
87     # Copy the RPMs/SRPMs from their output dir to the cache dir
88     shutil.copy(rpm_out_path, cache_dir)
89     shutil.copy(srpm_out_path, cache_dir)
90
91
92 # When run as a script, accept a set of builds and execute them
93 if __name__ == "__main__":
94     # Load RPM build variables from a YAML config file
95     build_vars_path = os.path.join(project_root, "build_vars.yaml")
96     with open(build_vars_path) as rpm_var_file:
97         build_vars = yaml.load(rpm_var_file)
98
99     # Accept the version(s) of the build(s) to perform as args
100     # TODO: More docs on ArgParser and argument
101     parser = argparse.ArgumentParser()
102     parser.add_argument("-v", "--version", action="append",
103                         metavar="major minor patch rpm", nargs="*", type=int,
104                         help="RPM version(s) to build")
105     parser.add_argument("-a", "--all", action="store_true",
106                         help="Build all RPMs")
107
108     # Print help if no arguments are given
109     if len(sys.argv) == 1:
110         parser.print_help()
111         sys.exit(1)
112
113     # Parse the given args
114     args = parser.parse_args()
115
116     # Build list of RPM builds to perform
117     builds = []
118     if args.all:
119         builds = build_vars["builds"]
120     else:
121         # Build a list of requested versions as dicts of version components
122         versions = []
123         version_keys = ["version_major", "version_minor", "version_patch",
124                         "rpm_release"]
125         # For each version arg, match all version components to build_vars name
126         for version in args.version:
127             versions.append(dict(zip(version_keys, version)))
128
129         # Find every RPM build that matches any version argument
130         # A passed version "matches" a build when the provided version
131         # components are a subset of the version components of a build. Any
132         # version components that aren't passed are simply not checked, so
133         # they can't fail the match, effectively wild-carding them.
134         for build in build_vars["builds"]:
135             for version in versions:
136                 # Converts both dicts' key:value pairs to lists of tuples and
137                 # checks that each tuple in the version list is present in the
138                 # build list.
139                 if all(item in build.items() for item in version.items()):
140                     builds.append(build)
141
142     for build in builds:
143         build_rpm(build)