Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new plugin: rust #579

Merged
merged 20 commits into from
Jul 27, 2016
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bzrignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ dist
htmlcov
__pycache__
docs/**.html
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!");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But oxide is still missing!

}
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()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing a clean_pull to wipe out the rust specific dir for the part made by the plugin (look at the nodejs plugin for inspiration)


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(["wget", "-N", "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"])
11 changes: 6 additions & 5 deletions snapcraft/tests/test_commands_list_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ class ListPluginsCommandTestCase(tests.TestCase):
# plugin list when wrapper at MAX_CHARACTERS_WRAP
default_plugin_output = (
'ant catkin copy gulp kbuild make nil python2 '
'scons \n'
'rust tar-content\n'
'autotools cmake go jdk kernel maven nodejs python3 '
'tar-content\n')
'scons\n')

@mock.patch('sys.stdout', new_callable=io.StringIO)
@mock.patch('subprocess.check_output')
Expand All @@ -43,9 +43,10 @@ def test_list_plugins_large_terminal(self, mock_subprocess, mock_stdout):
def test_list_plugins_small_terminal(self, mock_subprocess, mock_stdout):
mock_subprocess.return_value = "60"
expected_output = (
'ant cmake gulp kernel nil python3 \n'
'autotools copy jdk make nodejs scons \n'
'catkin go kbuild maven python2 tar-content\n')
'ant copy kbuild nil rust \n'
'autotools go kernel nodejs scons \n'
'catkin gulp make python2 tar-content\n'
'cmake jdk maven python3\n')
main(['list-plugins'])
self.assertEqual(mock_stdout.getvalue(), expected_output)

Expand Down
78 changes: 78 additions & 0 deletions snapcraft/tests/test_plugin_rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright (C) 2016 Canonical Ltd
#
# 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 MakePluginTestCase(tests.TestCase):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe update the test case class name?


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())
])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work so far!

Coveralls points out that you are missing some tests here. In line 84 of that Coveralls page for example, shows that there is no test for an invalid Rust channel.

I don't have commit access and Coveralls could be totally wrong, but I thought I should point it out just in case Coveralls is right.