Skip to content

Commit

Permalink
Add support for mesa libraries.
Browse files Browse the repository at this point in the history
Currently Snapcraft adds only a few standard paths to
LD_LIBRARY_PATH. This commit expands that to include
paths used by mesa packages.

Also add an opencv example to exercise this.

Signed-off-by: Kyle Fazzari <kyle@canonical.com>
  • Loading branch information
kyrofa committed Jan 6, 2016
1 parent 1045bd9 commit b6296cb
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 0 deletions.
14 changes: 14 additions & 0 deletions examples/opencv/snapcraft.yaml
@@ -0,0 +1,14 @@
name: opencv-example
version: 1.0
summary: Use OpenCV and OpenGL
description: A simple OpenCV example

binaries:
example:
exec: bin/example

parts:
example:
plugin: cmake
source: src
stage-packages: [libopencv-dev]
10 changes: 10 additions & 0 deletions examples/opencv/src/CMakeLists.txt
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 2.6)
project(example)

find_package(OpenCV REQUIRED)

add_executable(example main.cpp)

target_link_libraries(example ${OpenCV_LIBS})

install(TARGETS example RUNTIME DESTINATION bin)
15 changes: 15 additions & 0 deletions examples/opencv/src/main.cpp
@@ -0,0 +1,15 @@
#include <iostream>

#include <opencv2/core/core.hpp>

int main()
{
cv::Mat matrix(2, 2, CV_8UC1);

matrix.at<uint8_t>(cv::Point(0, 0)) = 1;
matrix.at<uint8_t>(cv::Point(0, 1)) = 2;
matrix.at<uint8_t>(cv::Point(1, 0)) = 3;
matrix.at<uint8_t>(cv::Point(1, 1)) = 4;

std::cout << matrix << std::endl;
}
5 changes: 5 additions & 0 deletions examples_tests/tests.py
Expand Up @@ -125,6 +125,11 @@ class TestSnapcraftExamples(testscenarios.TestWithScenarios):
'custom libpipeline called\n'
'include\n')],
}),
('opencv', {
'dir': 'opencv',
'name': 'opencv-example',
'version': '1.0',
}),
('py2-project', {
'dir': 'py2-project',
'name': 'spongeshaker',
Expand Down
102 changes: 102 additions & 0 deletions snapcraft/tests/test_yaml.py
Expand Up @@ -22,6 +22,7 @@

import fixtures

import snapcraft.common
import snapcraft.yaml
from snapcraft import (
dirs,
Expand Down Expand Up @@ -346,6 +347,107 @@ def test_config_expands_filesets(self, mock_loadPlugin):
})


class TestYamlEnvironment(tests.TestCase):

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

tempdirObj = tempfile.TemporaryDirectory()
self.addCleanup(tempdirObj.cleanup)
os.chdir(tempdirObj.name)
with open('icon.svg', 'w') as icon:
icon.write('<svg />')

with open('snapcraft.yaml', 'w') as fp:
fp.write("""name: test
version: "1"
summary: test
description: test
icon: icon.svg
parts:
part1:
plugin: go
stage-packages: [fswebcam]
""")

def test_config_runtime_environment(self):
config = snapcraft.yaml.Config()
environment = config.runtime_env('foo')
self.assertTrue('PATH="foo/bin:foo/usr/bin:$PATH"' in environment)

# Ensure that LD_LIBRARY_PATH is present and it contains only the
# basics.
paths = []
for variable in environment:
if 'LD_LIBRARY_PATH' in variable:
these_paths = variable.split('=')[1].strip()
paths.extend(these_paths.replace('"', '').split(':'))

self.assertTrue(len(paths) > 0,
'Expected LD_LIBRARY_PATH to be in environment')

arch = snapcraft.common.get_arch_triplet()
for expected in ['foo/lib', 'foo/usr/lib', 'foo/lib/{}'.format(arch),
'foo/usr/lib/{}'.format(arch)]:
self.assertTrue(expected in paths,
'Expected LD_LIBRARY_PATH to include "{}"'.format(
expected))

def test_config_runtime_environment_ld(self):
# Place a few ld.so.conf files in supported locations. We expect the
# contents of these to make it into the LD_LIBRARY_PATH.
os.makedirs('foo/usr/lib/my_arch/mesa/')
with open('foo/usr/lib/my_arch/mesa/ld.so.conf', 'w') as f:
f.write('/mesa')

os.makedirs('foo/usr/lib/my_arch/mesa-egl/')
with open('foo/usr/lib/my_arch/mesa-egl/ld.so.conf', 'w') as f:
f.write('# Standalone comment\n')
f.write('/mesa-egl')

config = snapcraft.yaml.Config()
environment = config.runtime_env('foo')

# Ensure that the LD_LIBRARY_PATH includes all the above paths
paths = []
for variable in environment:
if 'LD_LIBRARY_PATH' in variable:
these_paths = variable.split('=')[1].strip()
paths.extend(these_paths.replace('"', '').split(':'))

self.assertTrue(len(paths) > 0,
'Expected LD_LIBRARY_PATH to be in environment')

for expected in ['foo/mesa', 'foo/mesa-egl']:
self.assertTrue(expected in paths,
'Expected LD_LIBRARY_PATH to include "{}"'.format(
expected))


class TestLdLibraryPathParser(tests.TestCase):

def _write_conf_file(self, contents):
tmp = tempfile.NamedTemporaryFile(delete=False, mode='w')
self.addCleanup(os.remove, tmp.name)

tmp.write(contents)
tmp.close()

return tmp.name

def test_extract_ld_library_paths(self):
file_path = self._write_conf_file("""# This is a comment
/foo/bar
/colon:/separated,/comma\t/tab /space # This is another comment
/baz""")

self.assertEqual(['/foo/bar', '/colon', '/separated', '/comma',
'/tab', '/space', '/baz'],
snapcraft.yaml._extract_ld_library_paths(file_path))


class TestValidation(tests.TestCase):

def setUp(self):
Expand Down
43 changes: 43 additions & 0 deletions snapcraft/yaml.py
Expand Up @@ -20,6 +20,8 @@
import os
import os.path
import sys
import re
import glob

import jsonschema
import yaml
Expand Down Expand Up @@ -205,13 +207,22 @@ def runtime_env(self, root):
'{0}/usr/bin',
'$PATH'
]).format(root) + '"')

# Add the default LD_LIBRARY_PATH
env.append('LD_LIBRARY_PATH="' + ':'.join([
'{0}/lib',
'{0}/usr/lib',
'{0}/lib/{1}',
'{0}/usr/lib/{1}',
'$LD_LIBRARY_PATH'
]).format(root, common.get_arch_triplet()) + '"')

# Add more specific LD_LIBRARY_PATH if necessary
ld_library_paths = _determine_ld_library_path(root)
if ld_library_paths:
env.append('LD_LIBRARY_PATH="' + ':'.join(ld_library_paths) +
':$LD_LIBRARY_PATH"')

return env

def build_env(self, root):
Expand Down Expand Up @@ -292,6 +303,38 @@ def snap_env(self):
return env


def _determine_ld_library_path(root):
# If more ld.so.conf files need to be supported, add them here.
ld_config_globs = {
'{}/usr/lib/*/mesa*/ld.so.conf'.format(root)
}

ld_library_paths = []
for this_glob in ld_config_globs:
for ld_conf_file in glob.glob(this_glob):
ld_library_paths.extend(_extract_ld_library_paths(ld_conf_file))

return [root + path for path in ld_library_paths]


def _extract_ld_library_paths(ld_conf_file):
# From the ldconfig manpage, paths can be colon-, space-, tab-, newline-,
# or comma-separated.
path_delimiters = re.compile(r'[:\s,]')
comments = re.compile(r'#.*$')

paths = []
with open(ld_conf_file, 'r') as f:
for line in f:
# Remove comments from line
line = comments.sub('', line).strip()

if line:
paths.extend(path_delimiters.split(line))

return paths


def _validate_snapcraft_yaml(snapcraft_yaml):
schema_file = os.path.abspath(os.path.join(common.get_schemadir(),
'snapcraft.yaml'))
Expand Down

0 comments on commit b6296cb

Please sign in to comment.