1#
2#
3# Licensed to the Apache Software Foundation (ASF) under one
4# or more contributor license agreements.  See the NOTICE file
5# distributed with this work for additional information
6# regarding copyright ownership.  The ASF licenses this file
7# to you under the Apache License, Version 2.0 (the
8# "License"); you may not use this file except in compliance
9# with the License.  You may obtain a copy of the License at
10#
11#   http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing,
14# software distributed under the License is distributed on an
15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16# KIND, either express or implied.  See the License for the
17# specific language governing permissions and limitations
18# under the License.
19#
20#
21#
22# gen_make.py -- generate makefiles and dependencies
23#
24
25import os
26import stat
27import sys
28try:
29  # Python >=3.0
30  import configparser
31except ImportError:
32  # Python <3.0
33  import ConfigParser as configparser
34
35if sys.version_info[0] >= 3:
36  # Python >=3.0
37  from io import StringIO
38else:
39  # Python <3.0
40  try:
41    from cStringIO import StringIO
42  except ImportError:
43    from StringIO import StringIO
44
45import ezt
46
47import gen_base
48import generator.swig.header_wrappers
49import generator.swig.checkout_swig_header
50import generator.swig.external_runtime
51
52from gen_base import build_path_join, build_path_strip, build_path_splitfile, \
53      build_path_basename, build_path_dirname, build_path_retreat, unique
54
55
56def _normstr(x):
57  if os.sep == '/':
58    return os.path.normpath(str(x))
59  else:
60    return os.path.normpath(str(x).replace('/', os.sep)).replace(os.sep, '/')
61
62class Generator(gen_base.GeneratorBase):
63
64  _extension_map = {
65    ('exe', 'target'): '$(EXEEXT)',
66    ('exe', 'object'): '.lo',
67    ('lib', 'target'): '.la',
68    ('lib', 'object'): '.lo',
69    ('pyd', 'target'): '.la',
70    ('pyd', 'object'): '.lo',
71    ('so', 'target'): '.la',
72    ('so', 'object'): '.lo',
73    }
74
75  def __init__(self, fname, verfname, options=None):
76    gen_base.GeneratorBase.__init__(self, fname, verfname, options)
77    self.assume_shared_libs = False
78    if ('--assume-shared-libs', '') in options:
79      self.assume_shared_libs = True
80
81  def write(self):
82    install_deps = self.graph.get_deps(gen_base.DT_INSTALL)
83    install_sources = self.graph.get_all_sources(gen_base.DT_INSTALL)
84
85    cp = configparser.ConfigParser()
86    cp.read('gen-make.opts')
87    if cp.has_option('options', '--installed-libs'):
88      self.installed_libs = cp.get('options', '--installed-libs').split(',')
89    else:
90      self.installed_libs = []
91
92    # ensure consistency between runs
93    install_deps.sort()
94    install_sources.sort(key = lambda s: s.name)
95
96    class _eztdata(object):
97      def __init__(self, **kw):
98        vars(self).update(kw)
99
100    data = _eztdata(
101      modules=[ ],
102      swig_langs=[ ],
103      swig_c=[ ],
104      target=[ ],
105      itargets=[ ],
106      areas=[ ],
107      isources=[ ],
108      deps=[ ],
109      sql=[],
110      )
111
112    ########################################
113
114    for target in install_sources:
115      if isinstance(target, gen_base.TargetRaModule) or \
116         isinstance(target, gen_base.TargetFsModule):
117        # name of the module: strip 'libsvn_' and upper-case it
118        name = target.name[7:].upper()
119
120        # construct a list of the other .la libs to link against
121        retreat = build_path_retreat(target.path)
122        if target.name in self.installed_libs:
123          deps = []
124          link = [ '-l%s-%s' % (target.name[3:], self.version) ]
125        else:
126          deps = [ target.filename ]
127          link = [ build_path_join(retreat, target.filename) ]
128        for source in self.graph.get_sources(gen_base.DT_LINK, target.name):
129          if not isinstance(source, gen_base.TargetLib) or source.external_lib:
130            continue
131          elif source.name in self.installed_libs:
132            continue
133          deps.append(source.filename)
134          link.append(build_path_join(retreat, source.filename))
135
136        data.modules.append(_eztdata(name=name, deps=deps, link=link))
137
138    # write a list of directories in which things are built
139    #   get all the test scripts' directories
140    script_dirs = list(map(build_path_dirname, self.scripts + self.bdb_scripts))
141
142    #   remove duplicate directories between targets and tests
143    build_dirs = unique(self.target_dirs + script_dirs + self.swig_dirs)
144    data.build_dirs = build_dirs
145
146    # write lists of test files
147    # deps = all, progs = not including those marked "testing = skip"
148    data.bdb_test_deps = self.bdb_test_deps + self.bdb_scripts
149    data.bdb_test_progs = self.bdb_test_progs + self.bdb_scripts
150    data.test_deps = self.test_deps + self.scripts
151    data.test_progs = self.test_progs + self.scripts
152    data.test_helpers = self.test_helpers
153
154    # write list of all manpages
155    data.manpages = self.manpages
156
157    # write a list of files to remove during "make clean"
158    cfiles = [ ]
159    for target in install_sources:
160      # .la files are handled by the standard 'clean' rule; clean all the
161      # other targets
162      if not isinstance(target, gen_base.TargetScript) \
163         and not isinstance(target, gen_base.TargetProject) \
164         and not isinstance(target, gen_base.TargetI18N) \
165         and not isinstance(target, gen_base.TargetJava) \
166         and not target.external_lib \
167         and target.filename[-3:] != '.la':
168        cfiles.append(target.filename)
169    for script in self.scripts:
170      if script.endswith('.py'):
171        cfiles.append(script + 'c')
172    data.cfiles = sorted(cfiles)
173
174    # here are all the SQL files and their generated headers. the Makefile
175    # has an implicit rule for generating these, so there isn't much to do
176    # except to clean them out. we only do that for 'make extraclean' since
177    # these are included as part of the tarball. the files are transformed
178    # by gen-make, and developers also get a Make rule to keep them updated.
179    for hdrfile, sqlfile in sorted(self.graph.get_deps(gen_base.DT_SQLHDR),
180                                   key=lambda t: t[0]):
181      data.sql.append(_eztdata(header=hdrfile, source=sqlfile[0]))
182
183    data.release_mode = ezt.boolean(self.release_mode)
184
185    ########################################
186
187    if not self.release_mode:
188      swig_rules = StringIO()
189      for swig in (generator.swig.header_wrappers,
190                   generator.swig.checkout_swig_header,
191                   generator.swig.external_runtime):
192        gen = swig.Generator(self.conf, "swig")
193        gen.write_makefile_rules(swig_rules)
194
195      data.swig_rules = swig_rules.getvalue()
196
197    ########################################
198
199    # write dependencies and build rules for generated .c files
200    swig_c_deps = sorted(self.graph.get_deps(gen_base.DT_SWIG_C),
201                         key=lambda t: t[0].filename)
202
203    swig_lang_deps = {}
204    for lang in self.swig.langs:
205      swig_lang_deps[lang] = []
206
207    for objname, sources in swig_c_deps:
208      swig_lang_deps[objname.lang].append(str(objname))
209
210    for lang in self.swig.langs:
211      data.swig_langs.append(_eztdata(name=lang,
212                                      short=self.swig.short[lang],
213                                      short_upper=self.swig.short[lang].upper(),
214                                      deps=swig_lang_deps[lang]))
215
216    ########################################
217
218    if not self.release_mode:
219      for objname, sources in swig_c_deps:
220        data.swig_c.append(_eztdata(c_file=str(objname),
221                                    deps=list(map(str, sources)),
222                                    opts=self.swig.opts[objname.lang],
223                                    source=str(sources[0])))
224
225    ########################################
226
227    for target_ob in install_sources:
228
229      if isinstance(target_ob, gen_base.TargetScript):
230        # there is nothing to build
231        continue
232
233      target = target_ob.name
234      if isinstance(target_ob, gen_base.TargetJava):
235        path = target_ob.output_dir
236      else:
237        path = target_ob.path
238
239      retreat = build_path_retreat(path)
240
241      # get the source items (.o and .la) for the link unit
242      objects = [ ]
243      objdeps = [ ]
244      object_srcs = [ ]
245      headers = [ ]
246      header_classes = [ ]
247      header_class_filenames = [ ]
248      deps = [ ]
249      libs = [ ]
250      add_deps = target_ob.add_deps.split()
251
252      for link_dep in self.graph.get_sources(gen_base.DT_LINK, target_ob.name):
253        if isinstance(link_dep, gen_base.TargetJava):
254          deps.append(link_dep.name)
255        elif isinstance(link_dep, gen_base.TargetLinked):
256          if link_dep.external_lib:
257            libs.append(link_dep.external_lib)
258          elif link_dep.external_project:
259            # FIXME: This is a temporary workaround to fix build breakage
260            # expeditiously.  It is of questionable validity for a build
261            # node to have external_project but not have external_lib.
262            pass
263          elif link_dep.name in self.installed_libs:
264            libs.append('-l%s-%s' % (link_dep.name[3:], self.version))
265          else:
266            # append the output of the target to our stated dependencies
267            if not self.assume_shared_libs:
268              deps.append(link_dep.filename)
269
270            # link against the library
271            libs.append(build_path_join(retreat, link_dep.filename))
272        elif isinstance(link_dep, gen_base.ObjectFile):
273          # link in the object file
274          objects.append(link_dep.filename)
275          objdeps.append(_normstr(link_dep.filename))
276          for dep in self.graph.get_sources(gen_base.DT_OBJECT, link_dep, gen_base.SourceFile):
277            object_srcs.append(
278              build_path_join('$(abs_srcdir)', dep.filename))
279        elif isinstance(link_dep, gen_base.HeaderFile):
280          # link in the header file
281          # N.B. that filename_win contains the '_'-escaped class name
282          headers.append(link_dep.filename_win)
283          header_classes.append(link_dep.classname)
284          for dep in self.graph.get_sources(gen_base.DT_OBJECT, link_dep, gen_base.ObjectFile):
285            header_class_filenames.append(dep.filename)
286        else:
287          ### we don't know what this is, so we don't know what to do with it
288          raise UnknownDependency
289
290      for nonlib in self.graph.get_sources(gen_base.DT_NONLIB, target_ob.name):
291        if isinstance(nonlib, gen_base.TargetLinked):
292          if not nonlib.external_lib:
293            deps.append(nonlib.filename)
294
295      targ_varname = target.replace('-', '_')
296      objnames = build_path_strip(path, objects)
297
298      ezt_target = _eztdata(name=target_ob.name,
299                            varname=targ_varname,
300                            path=path,
301                            install=None,
302                            add_deps=add_deps,
303                            objects=objects,
304                            objdeps=objdeps,
305                            deps=deps,
306                            when=target_ob.when,
307                            )
308      data.target.append(ezt_target)
309
310      if hasattr(target_ob, 'link_cmd'):
311        ezt_target.link_cmd = target_ob.link_cmd
312      if hasattr(target_ob, 'output_dir'):
313        ezt_target.output_dir = target_ob.output_dir
314      if hasattr(target_ob, 'headers_dir'):
315        ezt_target.headers_dir = target_ob.headers_dir
316
317      # Add additional install dependencies if necessary
318      if target_ob.add_install_deps:
319        ezt_target.install = target_ob.install
320        ezt_target.install_deps = target_ob.add_install_deps
321
322      if isinstance(target_ob, gen_base.TargetJava):
323        ezt_target.type = 'java'
324        ezt_target.headers = headers
325        ezt_target.sources = None
326        ezt_target.jar = None
327        ezt_target.classes = target_ob.classes
328
329        # Build the headers from the header_classes with one 'javah' call
330        if headers:
331          ezt_target.header_class_filenames = header_class_filenames
332          ezt_target.header_classes = header_classes
333
334        # Build the objects from the object_srcs with one 'javac' call
335        if object_srcs:
336          ezt_target.sources = object_srcs
337
338        # Once the bytecodes have been compiled up, we produce the
339        # JAR.
340        if target_ob.jar:
341          ezt_target.jar_path = build_path_join(target_ob.classes,
342                                                target_ob.jar)
343          ezt_target.packages = target_ob.packages
344
345      elif isinstance(target_ob, gen_base.TargetI18N):
346        ezt_target.type = 'i18n'
347      else:
348        ezt_target.type = 'n/a'
349        ezt_target.filename = target_ob.filename
350        ezt_target.path = path
351        if (isinstance(target_ob, gen_base.TargetLib)
352            and not target_ob.undefined_lib_symbols):
353          ezt_target.undefined_flag = '$(LT_NO_UNDEFINED)'
354        else:
355          ezt_target.undefined_flag = ''
356        ezt_target.libs = gen_base.unique(libs)
357        ezt_target.objnames = objnames
358        ezt_target.basename = build_path_basename(target_ob.filename)
359
360    ########################################
361
362    for itype, i_targets in install_deps:
363
364      # perl bindings do their own thing, "swig-pl" target is
365      # already specified in Makefile.in
366      if itype == "swig-pl":
367        continue
368
369      outputs = [ ]
370
371      for t in i_targets:
372        if hasattr(t, 'filename'):
373          outputs.append(t.filename)
374
375      data.itargets.append(_eztdata(type=itype, outputs=outputs))
376
377    ########################################
378
379    # for each install group, write a rule to install its outputs
380    for area, inst_targets in install_deps:
381
382      # perl bindings do their own thing, "install-swig-pl" target is
383      # already specified in Makefile.in
384      if area == "swig-pl":
385        continue
386
387      # get the output files for these targets, sorted in dependency order
388      files = gen_base._sorted_files(self.graph, area)
389
390      ezt_area_type = (area == 'apache-mod' and 'mods-shared' or area)
391      ezt_area = _eztdata(type=ezt_area_type, files=[], extra_install=None)
392
393      def file_to_eztdata(file):
394          # cd to dirname before install to work around libtool 1.4.2 bug.
395          dirname, fname = build_path_splitfile(file.filename)
396          return _eztdata(mode=None,
397                          dirname=dirname, fullname=file.filename,
398                          filename=fname, when=file.when,
399                          pc_fullname=None,
400                          pc_installdir=None,
401                          pc_install_fname=None,)
402
403      def apache_file_to_eztdata(file):
404          # cd to dirname before install to work around libtool 1.4.2 bug.
405          dirname, fname = build_path_splitfile(file.filename)
406          base, ext = os.path.splitext(fname)
407          name = base.replace('mod_', '')
408          return _eztdata(mode='apache-mod',
409                          fullname=file.filename, dirname=dirname,
410                          name=name, filename=fname, when=file.when)
411
412      if area != 'test' and area != 'bdb-test':
413        data.areas.append(ezt_area)
414
415        area_var = area.replace('-', '_')
416        upper_var = area_var.upper()
417        ezt_area.varname = area_var
418        ezt_area.uppervar = upper_var
419
420        for file in files:
421          if isinstance(file.target, gen_base.TargetApacheMod):
422            ezt_file = apache_file_to_eztdata(file)
423          else:
424            ezt_file = file_to_eztdata(file)
425            if area == 'locale':
426              lang, objext = os.path.splitext(ezt_file.filename)
427              installdir = ('$(DESTDIR)$(%sdir)/%s/LC_MESSAGES'
428                            % (area_var, lang))
429              ezt_file.installdir = installdir
430              ezt_file.objext = objext
431            else:
432              ezt_file.install_fname = build_path_join('$(%sdir)' % area_var,
433                                                       ezt_file.filename)
434
435          # Install pkg-config files
436          if (isinstance(file.target, gen_base.TargetLib) and
437              ezt_file.fullname.startswith('subversion/libsvn_')):
438            ezt_file.pc_fullname = ezt_file.fullname.replace('-1.la', '.pc')
439            ezt_file.pc_installdir = '$(pkgconfig_dir)'
440            pc_install_fname = ezt_file.filename.replace('-1.la', '.pc')
441            ezt_file.pc_install_fname = build_path_join(ezt_file.pc_installdir,
442                                                        pc_install_fname)
443          ezt_area.files.append(ezt_file)
444
445        # certain areas require hooks for extra install rules defined
446        # in Makefile.in
447        ### we should turn AREA into an object, then test it instead of this
448        if area[:5] == 'swig-' and area[-4:] != '-lib' \
449           or area[:7] == 'javahl-' \
450           or area[:6] == 'svnxx-' \
451           or area == 'tools':
452          ezt_area.extra_install = 'yes'
453
454    ########################################
455
456    includedir = build_path_join('$(includedir)',
457                                 'subversion-%s' % self.version)
458    data.includes = [_eztdata(file=file,
459                              src=build_path_join('$(abs_srcdir)', file),
460                              dst=build_path_join(includedir,
461                                                  build_path_basename(file)))
462                      for file in self.includes]
463    data.includedir = includedir
464
465    ########################################
466
467    for target in install_sources:
468      if not isinstance(target, gen_base.TargetScript) and \
469         not isinstance(target, gen_base.TargetJava) and \
470         not isinstance(target, gen_base.TargetI18N):
471        data.isources.append(_eztdata(name=target.name,
472                                      filename=target.filename))
473
474    ########################################
475
476    # write dependencies and build rules (when not using suffix rules)
477    # for all other generated files which will not be installed
478    # (or will be installed, but not by the main generated build)
479    obj_deps = sorted(self.graph.get_deps(gen_base.DT_OBJECT),
480                      key=lambda t: t[0].filename)
481
482    for objname, sources in obj_deps:
483      dep = _eztdata(name=_normstr(objname),
484                     when=objname.when,
485                     deps=list(map(_normstr, sources)),
486                     cmd=objname.compile_cmd,
487                     source=_normstr(sources[0]))
488      data.deps.append(dep)
489      dep.generated = ezt.boolean(getattr(objname, 'source_generated', 0))
490
491    template = ezt.Template(os.path.join('build', 'generator', 'templates',
492                                         'build-outputs.mk.ezt'),
493                            compress_whitespace=False)
494    template.generate(open('build-outputs.mk', 'w'), data)
495
496    self.write_standalone()
497
498    self.write_transform_libtool_scripts(install_sources)
499
500    self.write_pkg_config_dot_in_files(install_sources)
501
502  def write_standalone(self):
503    """Write autogen-standalone.mk"""
504
505    standalone = open("autogen-standalone.mk", "w")
506    standalone.write('# DO NOT EDIT -- AUTOMATICALLY GENERATED '
507                     'BY build/generator/gen_make.py\n')
508    standalone.write('# FROM build-outputs.mk\n')
509    standalone.write('abs_srcdir = %s\n' % os.getcwd())
510    standalone.write('abs_builddir = %s\n' % os.getcwd())
511    standalone.write('top_srcdir = .\n')
512    standalone.write('top_builddir = .\n')
513    standalone.write('SWIG = swig\n')
514    standalone.write('SWIG_PY_OPTS = -python -py3 -nofastunpack -modern\n')
515    standalone.write('PYTHON = ' + sys.executable + '\n')
516    standalone.write('\n')
517    standalone.write(open("build-outputs.mk","r").read())
518    standalone.close()
519
520  def write_transform_libtool_scripts(self, install_sources):
521    """Write build/transform_libtool_scripts.sh"""
522    script = 'build/transform_libtool_scripts.sh'
523    fd = open(script, 'w')
524    fd.write('''#!/bin/sh
525# DO NOT EDIT -- AUTOMATICALLY GENERATED BY build/generator/gen_make.py
526
527transform()
528{
529  SCRIPT="$1"
530  LIBS="$2"
531  if [ -f $SCRIPT ]; then
532    if grep LD_PRELOAD "$SCRIPT" > /dev/null; then
533      :
534    elif grep LD_LIBRARY_PATH "$SCRIPT" > /dev/null; then
535      echo "Transforming $SCRIPT"
536      EXISTINGLIBS=""
537      for LIB in $LIBS; do
538        # exclude libsvn_test since the undefined test_funcs breaks libtool
539        case $LIB in
540          *libsvn_test-*) continue ;;
541        esac
542        if [ ! -f $LIB ]; then
543          continue
544        fi
545        if [ -z "$EXISTINGLIBS" ]; then
546          EXISTINGLIBS="$LIB"
547        else
548          EXISTINGLIBS="$EXISTINGLIBS $LIB"
549        fi
550      done
551      if [ ! -z "$EXISTINGLIBS" ]; then
552        cat "$SCRIPT" |
553        (
554          read LINE
555          echo "$LINE"
556          read LINE
557          echo "$LINE"
558          read LINE
559          echo "$LINE"
560          read LINE
561          echo "$LINE"
562          echo "LD_PRELOAD=\\"$EXISTINGLIBS\\""
563          echo "export LD_PRELOAD"
564          cat
565        ) < "$SCRIPT" > "$SCRIPT.new"
566        mv -f "$SCRIPT.new" "$SCRIPT"
567        chmod +x "$SCRIPT"
568      fi
569    fi
570  fi
571}
572
573DIR=`pwd`
574
575''')
576    libdep_cache = {}
577    paths = {}
578    for lib in ('libsvn_auth_gnome_keyring', 'libsvn_auth_kwallet'):
579      paths[lib] = self.sections[lib].options.get('path')
580    for target_ob in install_sources:
581      if not isinstance(target_ob, gen_base.TargetExe):
582        continue
583      name = target_ob.name
584      libs = self._get_all_lib_deps(target_ob.name, libdep_cache, paths)
585      path = paths[name]
586      for i in range(0, len(libs)):
587        lib = libs[i]
588        libpath = paths[libs[i]]
589        libs[i] = '$DIR/%s/.libs/%s-%s.so' % (libpath, lib, self.version)
590      fd.write('transform %s/%s "%s"\n' % (path, name, " ".join(libs)))
591    fd.close()
592    mode = stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH
593    os.chmod(script, mode)
594
595  def _get_all_lib_deps(self, target_name, libdep_cache, paths):
596    if not target_name in libdep_cache:
597      libs = set()
598      path = None
599      if target_name in self.sections:
600        section = self.sections[target_name]
601        opt_libs = self.sections[target_name].options.get('libs')
602        paths[target_name] = section.options.get('path')
603        if opt_libs:
604          for lib_name in opt_libs.split():
605            if lib_name.startswith('libsvn_'):
606              libs.add(lib_name)
607            for lib in self._get_all_lib_deps(lib_name, libdep_cache, paths):
608              libs.add(lib)
609      if target_name == 'libsvn_subr':
610        libs.update(('libsvn_auth_gnome_keyring', 'libsvn_auth_kwallet'))
611      libdep_cache[target_name] = sorted(libs)
612    return libdep_cache[target_name]
613
614  def write_pkg_config_dot_in_files(self, install_sources):
615    """Write pkg-config .pc.in files for Subversion libraries."""
616    for target_ob in install_sources:
617      if not (isinstance(target_ob, gen_base.TargetLib) and
618              target_ob.path.startswith('subversion/libsvn_')):
619        continue
620
621      lib_name = target_ob.name
622      lib_path = self.sections[lib_name].options.get('path')
623      lib_deps = self.sections[lib_name].options.get('libs')
624      lib_desc = self.sections[lib_name].options.get('description')
625      output_path = build_path_join(lib_path, lib_name + '.pc.in')
626      template = ezt.Template(os.path.join('build', 'generator', 'templates',
627                                           'pkg-config.in.ezt'),
628                              compress_whitespace=False)
629      class _eztdata(object):
630        def __init__(self, **kw):
631          vars(self).update(kw)
632
633      data = _eztdata(
634        lib_name=lib_name,
635        lib_desc=lib_desc,
636        lib_deps=[],
637        lib_required=[],
638        lib_required_private=[],
639        version=self.version,
640        )
641      # libsvn_foo -> -lsvn_foo-1
642      data.lib_deps.append('-l%s-%s' % (lib_name.replace('lib', '', 1), data.version))
643      for lib_dep in lib_deps.split():
644        if lib_dep == 'apriconv':
645          # apriconv is part of apr-util, skip it
646          continue
647        external_lib = self.sections[lib_dep].options.get('external-lib')
648        if external_lib:
649          ### Some of Subversion's internal libraries can appear as external
650          ### libs to handle conditional compilation. Skip these for now.
651          if external_lib in ['$(SVN_RA_LIB_LINK)', '$(SVN_FS_LIB_LINK)']:
652            continue
653          # If the external library is known to support pkg-config,
654          # add it to the Required: or Required.private: section.
655          # Otherwise, add the external library to linker flags.
656          pkg_config = self.sections[lib_dep].options.get('pkg-config')
657          if pkg_config:
658            private = self.sections[lib_dep].options.get('pkg-config-private')
659            if private:
660              data.lib_required_private.append(pkg_config)
661            else:
662              data.lib_required.append(pkg_config)
663          else:
664            # $(EXTERNAL_LIB) -> @EXTERNAL_LIB@
665            data.lib_deps.append('@%s@' % external_lib[2:-1])
666        else:
667          data.lib_required_private.append(lib_dep)
668
669      template.generate(open(output_path, 'w'), data)
670
671class UnknownDependency(Exception):
672  "We don't know how to deal with the dependent to link it in."
673  pass
674
675### End of file.
676