Support distro specific child templates
Support jinja child templates in renderspec/dist-templates which are automatically used with corresponding --spec-style and allow different output for each spec style (distro) using jinja `{% block %}` syntax. Also enable trim_blocks to keep resulting specs clean. Change-Id: I7c082e56836f06fb98881adc4cdf63d8626f4d7b
This commit is contained in:
parent
c49392043a
commit
563ba570a5
@ -163,6 +163,37 @@ With the `suse` spec-style::
|
||||
|
||||
License: Apache-2.0
|
||||
|
||||
|
||||
distribution specific blocks & child templates
|
||||
**********************************************
|
||||
|
||||
To properly handle differences between individual .spec styles, renderspec
|
||||
contains child templates in `renderspec/dist-templates` which are
|
||||
automatically used with corresponding `--spec-style`. These allow different
|
||||
output for each spec style (distro) using jinja `{% block %}` syntax.
|
||||
|
||||
For example consider simple `renderspec/dist-templates/fedora.spec.j2`:
|
||||
|
||||
{% extends ".spec" %}
|
||||
{% block build_requires %}
|
||||
BuildRequires: {{ py2pkg('setuptools') }}
|
||||
{% endblock %}
|
||||
|
||||
allows following in a spec template:
|
||||
|
||||
{% block build_requires %}{% endblock %}
|
||||
|
||||
to render into
|
||||
|
||||
BuildRequires: python-setuptools
|
||||
|
||||
with `fedora` spec style, while `renderspec/dist-templates/suse.spec.j2` might
|
||||
define other result for `suse` spec style.
|
||||
|
||||
For more information, see current `renderspec/dist-templates` and usage in
|
||||
`openstack/rpm-packaging`_ project.
|
||||
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/docs/dev/
|
||||
.. _openstack/rpm-packaging: https://git.openstack.org/cgit/openstack/rpm-packaging/
|
||||
.. _pymod2pkg: https://git.openstack.org/cgit/openstack/pymod2pkg
|
||||
|
@ -24,11 +24,11 @@ import sys
|
||||
from jinja2 import contextfilter
|
||||
from jinja2 import contextfunction
|
||||
from jinja2 import Environment
|
||||
from jinja2 import FileSystemLoader
|
||||
import pymod2pkg
|
||||
|
||||
import yaml
|
||||
|
||||
from renderspec.distloader import RenderspecLoader
|
||||
from renderspec import versions
|
||||
|
||||
|
||||
@ -147,12 +147,17 @@ def _env_register_filters_and_globals(env):
|
||||
|
||||
def generate_spec(spec_style, epochs, requirements, input_template_path):
|
||||
"""generate a spec file with the given style and the given template"""
|
||||
env = Environment(loader=FileSystemLoader(
|
||||
os.path.dirname(input_template_path)))
|
||||
|
||||
env = Environment(loader=RenderspecLoader(
|
||||
template_fn=input_template_path),
|
||||
trim_blocks=True)
|
||||
|
||||
_env_register_filters_and_globals(env)
|
||||
|
||||
template = env.get_template(os.path.basename(input_template_path))
|
||||
template_name = '.spec'
|
||||
if spec_style in env.loader.list_templates():
|
||||
template_name = spec_style
|
||||
template = env.get_template(template_name)
|
||||
return template.render(spec_style=spec_style, epochs=epochs,
|
||||
requirements=requirements)
|
||||
|
||||
|
4
renderspec/dist-templates/fedora.spec.j2
Normal file
4
renderspec/dist-templates/fedora.spec.j2
Normal file
@ -0,0 +1,4 @@
|
||||
{% extends ".spec" %}
|
||||
{% block build_requires %}
|
||||
BuildRequires: {{ py2pkg('setuptools') }}
|
||||
{% endblock %}
|
4
renderspec/dist-templates/suse.spec.j2
Normal file
4
renderspec/dist-templates/suse.spec.j2
Normal file
@ -0,0 +1,4 @@
|
||||
{% extends ".spec" %}
|
||||
{% block build_requires %}
|
||||
BuildRequires: openstack-macros
|
||||
{% endblock %}
|
76
renderspec/distloader.py
Normal file
76
renderspec/distloader.py
Normal file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2016 Red Hat
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import jinja2
|
||||
from jinja2.loaders import TemplateNotFound
|
||||
from jinja2.utils import open_if_exists
|
||||
import os
|
||||
|
||||
|
||||
def get_dist_templates_path():
|
||||
return os.path.join(os.path.dirname(__file__), 'dist-templates')
|
||||
|
||||
|
||||
class RenderspecLoader(jinja2.BaseLoader):
|
||||
"""A special template loader which allows rendering supplied .spec template
|
||||
with distro specific blocks maintained as part of renderspec.
|
||||
|
||||
'.spec' returns the spec template (which you need to supply during init)
|
||||
while other strings map to corresponding child templates included
|
||||
in renderspec which simply extend the '.spec' template.
|
||||
"""
|
||||
base_ref = '.spec'
|
||||
template_postfix = '.spec.j2'
|
||||
|
||||
def __init__(self, template_fn, encoding='utf-8'):
|
||||
self.base_fn = template_fn
|
||||
self.encoding = encoding
|
||||
self.disttemp_path = get_dist_templates_path()
|
||||
|
||||
def get_source(self, environment, template):
|
||||
if template == self.base_ref:
|
||||
fn = self.base_fn
|
||||
else:
|
||||
fn = os.path.join(self.disttemp_path,
|
||||
template + self.template_postfix)
|
||||
|
||||
f = open_if_exists(fn)
|
||||
if not f:
|
||||
return TemplateNotFound(template)
|
||||
try:
|
||||
contents = f.read().decode(self.encoding)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
mtime = os.path.getmtime(self.base_fn)
|
||||
|
||||
def uptodate():
|
||||
try:
|
||||
return os.path.getmtime(self.base_fn) == mtime
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
return contents, fn, uptodate
|
||||
|
||||
def list_templates(self):
|
||||
found = set([self.base_ref])
|
||||
walk_dir = os.walk(self.disttemp_path)
|
||||
for _, _, filenames in walk_dir:
|
||||
for fn in filenames:
|
||||
if fn.endswith(self.template_postfix):
|
||||
template = fn[:-len(self.template_postfix)]
|
||||
found.add(template)
|
||||
return sorted(found)
|
38
tests.py
38
tests.py
@ -24,7 +24,7 @@ from ddt import data, ddt, unpack
|
||||
|
||||
from jinja2 import Environment
|
||||
|
||||
from mock import Mock
|
||||
from mock import Mock, patch
|
||||
import os
|
||||
import renderspec
|
||||
import renderspec.versions
|
||||
@ -297,5 +297,41 @@ class RenderspecDistroDetection(unittest.TestCase):
|
||||
self.assertEqual(renderspec._get_default_distro(), "unknown")
|
||||
|
||||
|
||||
class RenderspecDistTeamplatesTests(unittest.TestCase):
|
||||
@patch('renderspec.distloader.get_dist_templates_path')
|
||||
def test_dist_templates(self, mock_dt_path):
|
||||
base_txt = ('Line before block\n'
|
||||
'{% block footest %}{% endblock %}\n'
|
||||
'Line after block\n')
|
||||
dt_txt = ('{% extends ".spec" %}'
|
||||
'{% block footest %}'
|
||||
'foo block\n'
|
||||
'macro: {{ py2pkg("test") }}\n'
|
||||
'{% endblock %}')
|
||||
expected_out = ('Line before block\n'
|
||||
'foo block\n'
|
||||
'macro: python-test\n'
|
||||
'Line after block')
|
||||
tmpdir = tempfile.mkdtemp(prefix='renderspec-test_')
|
||||
try:
|
||||
# create .spec template
|
||||
base_path = os.path.join(tmpdir, 'foo.spec.j2')
|
||||
with open(base_path, 'w+') as f:
|
||||
f.write(base_txt)
|
||||
# create custom dist template
|
||||
dt_dir = os.path.join(tmpdir, 'dist-templates')
|
||||
os.mkdir(dt_dir)
|
||||
dt_path = os.path.join(dt_dir, 'loldistro.spec.j2')
|
||||
with open(dt_path, 'w+') as f:
|
||||
f.write(dt_txt)
|
||||
# mock this to use testing dist-tempaltes folder
|
||||
mock_dt_path.return_value = dt_dir
|
||||
|
||||
out = renderspec.generate_spec('loldistro', {}, {}, base_path)
|
||||
self.assertEqual(out, expected_out)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user