parent
0e72e6adf9
commit
df761bd552
3 changed files with 1604 additions and 0 deletions
@ -0,0 +1,18 @@ |
||||
# Import our builders so they can be referenced in config as tito.builder.Class |
||||
# regardless of which submodule they're in. |
||||
|
||||
# flake8: noqa |
||||
|
||||
from fws_tito.builder.main import \ |
||||
Builder, \ |
||||
NoTgzBuilder, \ |
||||
GemBuilder, \ |
||||
UpstreamBuilder, \ |
||||
SatelliteBuilder, \ |
||||
MockBuilder, \ |
||||
BrewDownloadBuilder, \ |
||||
GitAnnexBuilder, \ |
||||
GitLfsBuilder, \ |
||||
MeadBuilder |
||||
|
||||
from fws_tito.builder.fetch import FetchBuilder |
@ -0,0 +1,212 @@ |
||||
# Copyright (c) 2008-2014 Red Hat, Inc. |
||||
# |
||||
# This software is licensed to you under the GNU General Public License, |
||||
# version 2 (GPLv2). There is NO WARRANTY for this software, express or |
||||
# implied, including the implied warranties of MERCHANTABILITY or FITNESS |
||||
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 |
||||
# along with this software; if not, see |
||||
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
||||
# |
||||
# Red Hat trademarks are not licensed under GPLv2. No permission is |
||||
# granted to use or replicate Red Hat trademarks that are incorporated |
||||
# in this software or its documentation. |
||||
|
||||
import re |
||||
import os.path |
||||
import shutil |
||||
|
||||
from tito.builder.main import BuilderBase |
||||
from tito.config_object import ConfigObject |
||||
from tito.common import error_out, debug, get_spec_version_and_release, \ |
||||
get_class_by_name |
||||
|
||||
|
||||
class FetchBuilder(ConfigObject, BuilderBase): |
||||
""" |
||||
A separate Builder class for projects whose source is not in git. Source |
||||
is fetched via a configurable strategy, which also determines what version |
||||
and release to insert into the spec file. |
||||
|
||||
Cannot build past tags. |
||||
""" |
||||
# TODO: test only for now, setup a tagger to fetch sources and store in git annex, |
||||
# then we can do official builds as well. |
||||
REQUIRED_ARGS = [] |
||||
|
||||
def __init__(self, name=None, tag=None, build_dir=None, |
||||
config=None, user_config=None, |
||||
args=None, **kwargs): |
||||
|
||||
BuilderBase.__init__(self, name=name, build_dir=build_dir, |
||||
config=config, |
||||
user_config=user_config, args=args, **kwargs) |
||||
|
||||
if tag: |
||||
error_out("FetchBuilder does not support building specific tags.") |
||||
|
||||
if not config.has_option("builder", |
||||
"fetch_strategy"): |
||||
print("WARNING: no fetch_strategy specified in tito.props" |
||||
", assuming ArgSourceStrategy.") |
||||
if not config.has_section("builder"): |
||||
config.add_section("builder") |
||||
config.set('builder', 'fetch_strategy', |
||||
'tito.builder.fetch.ArgSourceStrategy') |
||||
|
||||
self.build_tag = '%s-%s' % (self.project_name, |
||||
get_spec_version_and_release(self.start_dir, '%s.spec' % self.project_name)) |
||||
|
||||
def tgz(self): |
||||
self.ran_tgz = True |
||||
self._create_build_dirs() |
||||
|
||||
print("Fetching sources...") |
||||
source_strat_class = get_class_by_name(self.config.get( |
||||
'builder', 'fetch_strategy')) |
||||
source_strat = source_strat_class(self) |
||||
source_strat.fetch() |
||||
self.sources = source_strat.sources |
||||
self.spec_file = source_strat.spec_file |
||||
|
||||
def _get_rpmbuild_dir_options(self): |
||||
return ('--define "_topdir %s" --define "_sourcedir %s" --define "_builddir %s" ' |
||||
'--define "_srcrpmdir %s" --define "_rpmdir %s" ' % ( |
||||
self.rpmbuild_dir, |
||||
self.rpmbuild_sourcedir, self.rpmbuild_builddir, |
||||
self.rpmbuild_basedir, self.rpmbuild_basedir)) |
||||
|
||||
|
||||
class SourceStrategy(object): |
||||
""" |
||||
Base class for source strategies. These are responsible for fetching the |
||||
sources the builder will use, and determining what version/release we're |
||||
building. |
||||
|
||||
This is created and run in the tgz step of the builder. It will be passed |
||||
a reference to the builder calling it, which will be important for accessing |
||||
a lot of required information. |
||||
|
||||
Ideally sources and the spec file to be used should be copied into |
||||
builder.rpmbuild_sourcedir, which will be cleaned up automatically after |
||||
the builder runs. |
||||
""" |
||||
def __init__(self, builder): |
||||
""" |
||||
Defines fields that should be set when a sub-class runs fetch. |
||||
""" |
||||
self.builder = builder |
||||
|
||||
# Full path to the spec file we'll actually use to build, should be a |
||||
# copy, never a live spec file in a git repo as sometimes it will be |
||||
# modified: |
||||
self.spec_file = None |
||||
|
||||
# Will contain the full path to each source we gather: |
||||
self.sources = [] |
||||
|
||||
# The version we're building: |
||||
self.version = None |
||||
|
||||
# The release we're building: |
||||
self.release = None |
||||
|
||||
def fetch(self): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ArgSourceStrategy(SourceStrategy): |
||||
""" |
||||
Assumes the builder was passed an explicit argument specifying which source |
||||
file(s) to use. |
||||
""" |
||||
def fetch(self): |
||||
|
||||
# Assuming we're still in the start directory, get the absolute path |
||||
# to all sources specified: |
||||
# TODO: support passing of multiple sources here. |
||||
# TODO: error out if not present |
||||
manual_sources = [self.builder.args['source'][0]] |
||||
debug("Got sources: %s" % manual_sources) |
||||
|
||||
# Copy the live spec from our starting location. Unlike most builders, |
||||
# we are not using a copy from a past git commit. |
||||
self.spec_file = os.path.join(self.builder.rpmbuild_sourcedir, |
||||
'%s.spec' % self.builder.project_name) |
||||
shutil.copyfile( |
||||
os.path.join(self.builder.start_dir, '%s.spec' % |
||||
self.builder.project_name), |
||||
self.spec_file) |
||||
print(" %s.spec" % self.builder.project_name) |
||||
|
||||
# TODO: Make this a configurable strategy: |
||||
i = 0 |
||||
replacements = [] |
||||
for s in manual_sources: |
||||
base_name = os.path.basename(s) |
||||
dest_filepath = os.path.join(self.builder.rpmbuild_sourcedir, |
||||
base_name) |
||||
shutil.copyfile(s, dest_filepath) |
||||
self.sources.append(dest_filepath) |
||||
|
||||
# Add a line to replace in the spec for each source: |
||||
source_regex = re.compile("^(source%s:\s*)(.+)$" % i, re.IGNORECASE) |
||||
new_line = "Source%s: %s\n" % (i, base_name) |
||||
replacements.append((source_regex, new_line)) |
||||
|
||||
# Replace version and release in spec: |
||||
version_regex = re.compile("^(version:\s*)(.+)$", re.IGNORECASE) |
||||
release_regex = re.compile("^(release:\s*)(.+)$", re.IGNORECASE) |
||||
|
||||
(self.version, self.release) = self._get_version_and_release() |
||||
print("Building version: %s" % self.version) |
||||
print("Building release: %s" % self.release) |
||||
replacements.append((version_regex, "Version: %s\n" % self.version)) |
||||
replacements.append((release_regex, "Release: %s\n" % self.release)) |
||||
|
||||
self.replace_in_spec(replacements) |
||||
|
||||
def _get_version_and_release(self): |
||||
""" |
||||
Get the version and release from the builder. |
||||
Sources are configured at this point. |
||||
""" |
||||
# Assuming source0 is a tar.gz we can extract a version and possibly |
||||
# release from: |
||||
base_name = os.path.basename(self.sources[0]) |
||||
debug("Extracting version/release from: %s" % base_name) |
||||
|
||||
# usually a source tarball won't have a release, that is an RPM concept. |
||||
# Don't forget dist! |
||||
release = "1%{?dist}" |
||||
|
||||
# Example filename: tito-0.4.18.tar.gz: |
||||
simple_version_re = re.compile(".*-(.*).(tar.gz|tgz|zip|bz2)") |
||||
match = re.search(simple_version_re, base_name) |
||||
if match: |
||||
version = match.group(1) |
||||
else: |
||||
error_out("Unable to determine version from file: %s" % base_name) |
||||
|
||||
return (version, release) |
||||
|
||||
def replace_in_spec(self, replacements): |
||||
""" |
||||
Replace lines in the spec file using the given replacements. |
||||
|
||||
Replacements are a tuple of a regex to look for, and a new line to |
||||
substitute in when the regex matches. |
||||
|
||||
Replaces all lines with one pass through the file. |
||||
""" |
||||
in_f = open(self.spec_file, 'r') |
||||
out_f = open(self.spec_file + ".new", 'w') |
||||
for line in in_f.readlines(): |
||||
for line_regex, new_line in replacements: |
||||
match = re.match(line_regex, line) |
||||
if match: |
||||
line = new_line |
||||
out_f.write(line) |
||||
|
||||
in_f.close() |
||||
out_f.close() |
||||
shutil.move(self.spec_file + ".new", self.spec_file) |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue