diff --git a/integration_tests/snaps/scriptlet-prepare/snapcraft.yaml b/integration_tests/snaps/scriptlet-prepare/snapcraft.yaml new file mode 100644 index 0000000000..3e76da9cb8 --- /dev/null +++ b/integration_tests/snaps/scriptlet-prepare/snapcraft.yaml @@ -0,0 +1,31 @@ +name: prepare-scriptlet-test +version: '0.1' +summary: Runs the prepare scriptlet for a part +description: | + Runs the shell script defined in `prepare` before the build takes place + creating a `Makefile` for the make plugin to execute on. + +grade: devel +confinement: devmode + +parts: + prepare-scriptlet-test: + source: . + plugin: make + prepare: | + echo "all:" > Makefile + echo '\techo building' >> Makefile + echo "install:" >> Makefile + echo '\ttouch $(DESTDIR)/prepared' >> Makefile + + prepare-and-install: + source: . + plugin: make + prepare: | + echo 'all:' > Makefile + echo '\ttouch built' >> Makefile + echo 'install:' >> Makefile + echo '\ttouch installed' >> Makefile + install: | + cp built $SNAPCRAFT_PART_INSTALL/ + cp installed $SNAPCRAFT_PART_INSTALL/ diff --git a/integration_tests/test_scriptlets.py b/integration_tests/test_scriptlets.py index c55578d9e1..87147a6ad2 100644 --- a/integration_tests/test_scriptlets.py +++ b/integration_tests/test_scriptlets.py @@ -23,6 +23,23 @@ class ScriptletTestCase(integration_tests.TestCase): + def test_prepare_scriptlet(self): + project_dir = 'scriptlet-prepare' + self.run_snapcraft('build', project_dir) + + installdir = os.path.join( + project_dir, 'parts', 'prepare-scriptlet-test', 'install') + prepared_file_path = os.path.join(installdir, 'prepared') + + installdir = os.path.join( + project_dir, 'parts', 'prepare-and-install', 'install') + built_file_path = os.path.join(installdir, 'built') + installed_file_path = os.path.join(installdir, 'installed') + + self.assertThat(prepared_file_path, FileExists()) + self.assertThat(built_file_path, FileExists()) + self.assertThat(installed_file_path, FileExists()) + def test_install_scriptlet(self): project_dir = 'scriptlet-install' self.run_snapcraft('build', project_dir) diff --git a/schema/snapcraft.yaml b/schema/snapcraft.yaml index 816038585d..d12cf2557a 100644 --- a/schema/snapcraft.yaml +++ b/schema/snapcraft.yaml @@ -210,6 +210,9 @@ properties: install: type: string default: '' + prepare: + type: string + default: '' plugs: type: object slots: diff --git a/snapcraft/__init__.py b/snapcraft/__init__.py index 435c82f4bd..e09b6e985c 100644 --- a/snapcraft/__init__.py +++ b/snapcraft/__init__.py @@ -225,6 +225,18 @@ `prime/` directory reflects the file structure of the snap with no extraneous content). + - prepare: shell script + + If present, the shell script defined here is run before the `build` step + of the plugin starts. The working directory is the base build + directory for the given part. The defined script is run with `/bin/sh`. + + For example: + + prepare: | + cd scripts + ./bootstrap.sh + - install: shell script If present, the shell script defined here is run after the `build` step diff --git a/snapcraft/internal/pluginhandler/__init__.py b/snapcraft/internal/pluginhandler/__init__.py index ae6dd03aed..58e47cf292 100644 --- a/snapcraft/internal/pluginhandler/__init__.py +++ b/snapcraft/internal/pluginhandler/__init__.py @@ -389,8 +389,9 @@ def ignore(directory, files): script_runner = ScriptRunner(builddir=self.code.build_basedir) + script_runner.run(scriptlet=self._part_properties.get('prepare')) self.code.build() - script_runner.install(scriptlet=self._part_properties.get('install')) + script_runner.run(scriptlet=self._part_properties.get('install')) self.mark_build_done() diff --git a/snapcraft/internal/pluginhandler/_scriptlets.py b/snapcraft/internal/pluginhandler/_scriptlets.py index 31ea191b69..5b26f5696d 100644 --- a/snapcraft/internal/pluginhandler/_scriptlets.py +++ b/snapcraft/internal/pluginhandler/_scriptlets.py @@ -27,7 +27,8 @@ class ScriptRunner: def __init__(self, *, builddir): self._builddir = builddir - def _run(self, scriptlet): + def run(self, *, scriptlet): + """Runs the specified scriptlet.""" if not scriptlet: return @@ -43,11 +44,3 @@ def _run(self, scriptlet): finally: with suppress(FileNotFoundError): os.unlink(scriptlet_path) - - def install(self, *, scriptlet): - """Runs post build procedures for a part. - - Every plugin has a `build` step, this method runs - whatever is in the part's install keyword as a shell - """ - self._run(scriptlet) diff --git a/snapcraft/tests/pluginhandler/test_scriptlets.py b/snapcraft/tests/pluginhandler/test_scriptlets.py index 6e05d93332..8c9b3cff84 100644 --- a/snapcraft/tests/pluginhandler/test_scriptlets.py +++ b/snapcraft/tests/pluginhandler/test_scriptlets.py @@ -24,6 +24,16 @@ class ScriptletTestCase(tests.TestCase): + def test_run_prepare_scriptlet(self): + handler = mocks.loadplugin( + 'test-part', part_properties={'prepare': 'touch before-build'}) + + handler.build() + + before_build_file_path = os.path.join(handler.code.build_basedir, + 'before-build') + self.assertThat(before_build_file_path, FileExists()) + def test_run_install_scriptlet(self): handler = mocks.loadplugin( 'test-part', part_properties={'install': 'touch after-build'})