1#! /usr/bin/env python 2# encoding: utf-8 3# Calle Rosenquist, 2017 (xbreak) 4""" 5Create task that copies source files to the associated build node. 6This is useful to e.g. construct a complete Python package so it can be unit tested 7without installation. 8 9Source files to be copied can be specified either in `buildcopy_source` attribute, or 10`source` attribute. If both are specified `buildcopy_source` has priority. 11 12Examples:: 13 14 def build(bld): 15 bld(name = 'bar', 16 features = 'py buildcopy', 17 source = bld.path.ant_glob('src/bar/*.py')) 18 19 bld(name = 'py baz', 20 features = 'buildcopy', 21 buildcopy_source = bld.path.ant_glob('src/bar/*.py') + ['src/bar/resource.txt']) 22 23""" 24import os, shutil 25from waflib import Errors, Task, TaskGen, Utils, Node, Logs 26 27@TaskGen.before_method('process_source') 28@TaskGen.feature('buildcopy') 29def make_buildcopy(self): 30 """ 31 Creates the buildcopy task. 32 """ 33 def to_src_nodes(lst): 34 """Find file nodes only in src, TaskGen.to_nodes will not work for this since it gives 35 preference to nodes in build. 36 """ 37 if isinstance(lst, Node.Node): 38 if not lst.is_src(): 39 raise Errors.WafError('buildcopy: node %s is not in src'%lst) 40 if not os.path.isfile(lst.abspath()): 41 raise Errors.WafError('buildcopy: Cannot copy directory %s (unsupported action)'%lst) 42 return lst 43 44 if isinstance(lst, str): 45 lst = [x for x in Utils.split_path(lst) if x and x != '.'] 46 47 node = self.bld.path.get_src().search_node(lst) 48 if node: 49 if not os.path.isfile(node.abspath()): 50 raise Errors.WafError('buildcopy: Cannot copy directory %s (unsupported action)'%node) 51 return node 52 53 node = self.bld.path.get_src().find_node(lst) 54 if node: 55 if not os.path.isfile(node.abspath()): 56 raise Errors.WafError('buildcopy: Cannot copy directory %s (unsupported action)'%node) 57 return node 58 raise Errors.WafError('buildcopy: File not found in src: %s'%os.path.join(*lst)) 59 60 nodes = [ to_src_nodes(n) for n in getattr(self, 'buildcopy_source', getattr(self, 'source', [])) ] 61 if not nodes: 62 Logs.warn('buildcopy: No source files provided to buildcopy in %s (set `buildcopy_source` or `source`)', 63 self) 64 return 65 node_pairs = [(n, n.get_bld()) for n in nodes] 66 self.create_task('buildcopy', [n[0] for n in node_pairs], [n[1] for n in node_pairs], node_pairs=node_pairs) 67 68class buildcopy(Task.Task): 69 """ 70 Copy for each pair `n` in `node_pairs`: n[0] -> n[1]. 71 72 Attribute `node_pairs` should contain a list of tuples describing source and target: 73 74 node_pairs = [(in, out), ...] 75 76 """ 77 color = 'PINK' 78 79 def keyword(self): 80 return 'Copying' 81 82 def run(self): 83 for f,t in self.node_pairs: 84 t.parent.mkdir() 85 shutil.copy2(f.abspath(), t.abspath()) 86