Skip to content

Commit

Permalink
Add new plugin: rust
Browse files Browse the repository at this point in the history
  • Loading branch information
mariogrip authored and bgdncz committed Jul 21, 2016
1 parent ece292d commit 427e3a6
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .bzrignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ htmlcov
__pycache__
docs/**.html
*.swp
Cargo.lock
target
6 changes: 6 additions & 0 deletions integration_tests/snaps/simple-rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "simple-rust"
version = "0.1.0"
authors = ["Marius Gripsgard <mariogrip@ubuntu.com>"]

[dependencies]
10 changes: 10 additions & 0 deletions integration_tests/snaps/simple-rust/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: test-package
version: 0.1
summary: A simple rust project.
description: A simple rust project.
confinement: strict

parts:
simple-rust:
plugin: rust
source: .
3 changes: 3 additions & 0 deletions integration_tests/snaps/simple-rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("There is rust on snaps!");
}
25 changes: 25 additions & 0 deletions integration_tests/test_rust_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (C) 2016 Marius Gripsgard (mariogrip@ubuntu.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os, integration_tests

class RustPluginTestCase(integration_tests.TestCase):

def test_stage_rust_plugin(self):
project_dir = 'simple-rust'
self.run_snapcraft('stage', project_dir)

binary_output = self.get_output_ignoring_non_zero_exit(
os.path.join('stage', 'bin', 'simple-rust'), cwd=project_dir)
self.assertEqual("There is rust on snaps!\n", binary_output)
102 changes: 102 additions & 0 deletions snapcraft/plugins/rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Copyright (C) 2016 Marius Gripsgard (mariogrip@ubuntu.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""This rust plugin is useful for building rust based parts.
Rust uses cargo to drive the build.
This plugin uses the common plugin keywords as well as those for "sources".
For more information check the 'plugins' topic for the former and the
'sources' topic for the latter.
Additionally, this plugin uses the following plugin-specific keywords:
- rust-channel
(string)
select rust channel (stable, beta, nightly)
- rust-revision
(string)
select rust version
"""

import os
import snapcraft
import shutil


class RustPlugin(snapcraft.BasePlugin):

@classmethod
def schema(cls):
schema = super().schema()
schema['properties']['rust-channel'] = {
'type': 'string',
}
schema['properties']['rust-revision'] = {
'type': 'string',
}
return schema

def __init__(self, name, options, project):
super().__init__(name, options, project)
self._rustpath = os.path.join(self.partdir, "rust")
self._rustc = os.path.join(self._rustpath, "bin", "rustc")
self._rustdoc = os.path.join(self._rustpath, "bin", "rustdoc")
self._cargo = os.path.join(self._rustpath, "bin", "cargo")
self._rustlib = os.path.join(self._rustpath, "lib")

def build(self):
super().build()
self.run([self._cargo, "install",
"-j{}".format(self.project.parallel_build_count),
"--root", self.installdir], env=self._build_env())

def _build_env(self):
env = os.environ.copy()
env.update({"RUSTC": self._rustc,
"RUSTDOC": self._rustdoc,
"RUST_PATH": self._rustlib})
return env

def pull(self):
super().pull()
self._fetch_rust()

def clean_pull(self):
super().clean_pull()

# Remove the rust path (if any)
if os.path.exists(self._rustpath):
shutil.rmtree(self._rustpath)

def _fetch_rust(self):
options = []

if self.options.rust_revision:
options.append("--revision=%s" % self.options.rust_revision)

if self.options.rust_channel:
if self.options.rust_channel in ["stable", "beta", "nightly"]:
options.append("--channel=%s" % self.options.rust_channel)
else:
raise EnvironmentError("%s is not a valid rust channel"
% self.options.rust_channel)

rustup = "rustup.sh"
self.run(["curl", "https://static.rust-lang.org/rustup.sh",
"-o", rustup])
self.run(["chmod", "+x", rustup])
self.run(["./%s" % rustup,
"--prefix=%s" % self._rustpath,
"--disable-sudo", "--save"])
2 changes: 1 addition & 1 deletion snapcraft/tests/test_commands_list_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ListPluginsCommandTestCase(tests.TestCase):
'ant catkin copy gradle jdk kernel maven '
'nodejs python2 qmake tar-content\n'
'autotools cmake go gulp kbuild make nil '
'plainbox-provider python3 scons\n')
'plainbox-provider python3 scons rust\n')

def test_list_plugins_non_tty(self):
fake_terminal = fixture_setup.FakeTerminal(isatty=False)
Expand Down
97 changes: 97 additions & 0 deletions snapcraft/tests/test_plugin_rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Copyright (C) 2016 Marius Gripsgard (mariogrip@ubuntu.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os

from unittest import mock

import snapcraft
from snapcraft import tests
from snapcraft.plugins import rust


class RustPluginTestCase(tests.TestCase):

def setUp(self):
super().setUp()

class Options:
makefile = None
make_parameters = []

self.options = Options()
self.project_options = snapcraft.ProjectOptions()

def test_schema(self):
schema = rust.RustPlugin.schema()

properties = schema['properties']
self.assertTrue('rust-channel' in properties,
'Expected "rust-channel" to be included in properties')
self.assertTrue('rust-revision' in properties,
'Expected "rust-revision to be included in properties')

rust_channel = properties['rust-channel']
self.assertTrue('type' in rust_channel,
'Expected "type" to be included in "rust-channel"')

rust_channel_type = rust_channel['type']
self.assertEqual(rust_channel_type, 'string',
'Expected "rust-channel" "type" to be "string", '
'but it was "{}"'.format(rust_channel_type))

rust_revision = properties['rust-revision']
self.assertTrue('type' in rust_revision,
'Expected "type" to be included in "rust-revision"')

rust_revision_type = rust_revision['type']
self.assertEqual(rust_revision_type, 'string',
'Expected "rust-revision" "type" to be "string", '
'but it was "{}"'.format(rust_revision_type))

@mock.patch.object(rust.RustPlugin, 'run')
def test_build(self, run_mock):
plugin = rust.RustPlugin('test-part', self.options,
self.project_options)
os.makedirs(plugin.sourcedir)

plugin.build()

self.assertEqual(1, run_mock.call_count)
run_mock.assert_has_calls([
mock.call([plugin._cargo, 'install',
'-j{}'.format(plugin.project.parallel_build_count),
'--root', plugin.installdir], env=plugin._build_env())
])

@mock.patch.object(rust.RustPlugin, 'run')
def test_pull(self, run_mock):
plugin = rust.RustPlugin('test-part', self.options,
self.project_options)
os.makedirs(plugin.sourcedir)
plugin.options.rust_revision = []
plugin.options.rust_channel = []

plugin.pull()
rustup = "rustup.sh"

self.assertEqual(3, run_mock.call_count)
run_mock.assert_has_calls([
mock.call(["curl", "https://static.rust-lang.org/rustup.sh",
"-o", rustup]),
mock.call(["chmod", "+x", rustup]),
mock.call(["./%s" % rustup,
"--prefix=%s" % plugin._rustpath,
"--disable-sudo", "--save"])
])

0 comments on commit 427e3a6

Please sign in to comment.