Abstract all RDF stuff into 3 functions.
[lv2.git] / lv2include / lv2include.py
blobf4b9b3c51c88c81f99f46d0dc6995908526950da
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 """A program (and Python module) to generate a tree of symlinks to LV2
5 extension bundles, where the path of the symlink corresponds to the URI of
6 the extension. This allows including extension headers in code without using
7 the bundle name. Including extension headers in this way is much better,
8 since there is no dependency on the (meaningless and non-persistent) bundle
9 name in the code using the header.
11 For example, after running lv2includegen (and setting the compiler include
12 path appropriately), LV2 headers could be included like so:
14 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
15 #include "lv2/lv2plug.in/ns/ext/event/event.h"
16 #include "lv2/example.org/foo/foo.h"
18 Where the initial "lv2" is arbitrary; in this case lv2includegen's output
19 directory was "lv2", and that directory's parent was added to the compiler
20 include search path. It is a good idea to use such a prefix directory so
21 domain names do not conflict with anything else in the include path.
22 """
24 __authors__ = 'David Robillard'
25 __license = 'GNU GPL v3 or later <http://www.gnu.org/licenses/gpl.html>'
26 __contact__ = 'devel@lists.lv2plug.in'
27 __date__ = '2010-10-05'
29 import errno
30 import glob
31 import os
32 import stat
33 import sys
35 import RDF
37 def rdf_load(uri):
38 model = RDF.Model()
39 parser = RDF.Parser(name="guess")
40 parser.parse_into_model(model, uri)
41 return model
43 def rdf_find(model, s, p, o):
44 return model.find_statements(RDF.Statement(s, p, o))
46 def rdf_namespace(uri):
47 return RDF.NS(uri)
49 rdf = rdf_namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
50 lv2 = rdf_namespace('http://lv2plug.in/ns/lv2core#')
52 def lv2_path():
53 "Return the LV2 search path (LV2_PATH in the environment, or a default)."
54 if 'LV2_PATH' in os.environ:
55 return os.environ['LV2_PATH']
56 else:
57 ret = '/usr/lib/lv2' + os.pathsep + '/usr/local/lib/lv2'
58 print 'LV2_PATH unset, using default ' + ret
59 return ret
61 def __bundles(search_path):
62 "Return a list of all LV2 bundles found in search_path."
63 dirs = search_path.split(os.pathsep)
64 bundles = []
65 for dir in dirs:
66 bundles += glob.glob(os.path.join(dir, '*.lv2'))
67 return bundles
69 def __usage():
70 script = os.path.basename(sys.argv[0])
71 print """Usage:
72 %s OUTDIR
74 OUTDIR : Directory to build include tree
76 Example:
77 %s /usr/local/include/lv2
78 """ % (script, script)
80 def __mkdir_p(path):
81 "Equivalent of UNIX mkdir -p"
82 try:
83 os.makedirs(path)
84 except OSError as e:
85 if e.errno == errno.EEXIST:
86 pass
87 else:
88 raise
90 def build_tree(search_path, outdir):
91 """Build a directory tree under outdir containing symlinks to all LV2
92 extensions found in search_path, such that the symlink paths correspond to
93 the extension URIs."""
94 for bundle in __bundles(search_path):
95 # Load manifest into model
96 manifest = rdf_load('file://' + os.path.join(bundle, 'manifest.ttl'))
98 # Query extension URI
99 results = rdf_find(manifest, None, rdf.type, lv2.Specification)
100 for r in results:
101 ext_uri = str(r.subject.uri)
102 ext_scheme = ext_uri[0:ext_uri.find(':')]
103 ext_path = os.path.normpath(ext_uri[ext_uri.find(':') + 1:].lstrip('/'))
104 ext_dir = os.path.join(outdir, ext_scheme, ext_path)
106 # Make parent directories
107 __mkdir_p(os.path.dirname(ext_dir))
109 # Remove existing symlink if necessary
110 if os.access(ext_dir, os.F_OK):
111 mode = os.lstat(ext_dir)[stat.ST_MODE]
112 if stat.S_ISLNK(mode):
113 os.remove(ext_dir)
114 else:
115 raise Exception(ext_dir + " exists and is not a link")
117 # Make symlink to bundle directory
118 os.symlink(bundle, ext_dir)
120 if __name__ == "__main__":
121 args = sys.argv[1:]
122 if len(args) != 1:
123 __usage()
124 sys.exit(1)
126 outdir = args[0]
127 print "Building LV2 include tree at", outdir
129 build_tree(lv2_path(), outdir)