1############################################################################### 2# Copyright (c) 2013 INRIA 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License version 2 as 6# published by the Free Software Foundation; 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16# 17# Authors: Daniel Camara <daniel.camara@inria.fr> 18# Mathieu Lacage <mathieu.lacage@sophia.inria.fr> 19############################################################################### 20''' 21 ModuleSource.py 22 23 This file stores the real source fetching implementations for each one of 24 the handled source code repository tools. It is this class that defines how 25 a download of a zip file, or a mercurial repository will be made. 26''' 27import urllib 28try: 29 from urllib.parse import urlparse 30 from urllib.request import urlretrieve 31except ImportError: 32 from urlparse import urlparse 33 from urllib import urlretrieve 34import bake.Utils 35from bake.Exceptions import TaskError 36from bake.Utils import ModuleAttributeBase 37import os 38import re 39import platform 40try: 41 import distro 42except ImportError: 43 import sys 44 print(">>> Error: missing Python 'distro' package; please install") 45 sys.exit(1) 46import subprocess 47import importlib 48try: 49 import commands 50 from commands import getoutput 51except ImportError: 52 from subprocess import getoutput 53from datetime import date 54 55class ModuleSource(ModuleAttributeBase): 56 """ Generic Source class, stores the generic methods for the source 57 code repository tools. 58 """ 59 60 def __init__(self): 61 """ Generic attributes definition.""" 62 63 ModuleAttributeBase.__init__(self) 64 self.add_attribute('module_directory', '', 'Source module directory', 65 mandatory=False) 66 self.add_attribute('patch', '', 'code to patch after download', 67 mandatory=False) 68 69 self.add_attribute('new_variable', '', 'Appends the value to the' 70 ' system variable on the format VARIABLE1=value1' 71 ';VARIABLE2=value2', mandatory=False) 72 73 self.add_attribute('post_download', '', 'UNIX Command to run' 74 ' after the download', mandatory=False) 75 76 @classmethod 77 def subclasses(self): 78 return ModuleSource.__subclasses__() 79 80 @classmethod 81 def create(cls, name): 82 """Instantiates the class that is called by the requested name.""" 83 84 # Runs over all the Classes and instantiates the one that has the name 85 # equals to the name passed as parameter 86 for subclass in ModuleSource.subclasses(): 87 if subclass.name() == name: 88 return subclass() 89 return None 90 91 # Methods to be implemented by the inherited classes 92 def diff(self, env): 93 raise NotImplemented() 94 def download(self, env): 95 raise NotImplemented() 96 def update(self, env): 97 raise NotImplemented() 98 def check_version(self, env): 99 raise NotImplemented() 100 101 def perform_post_download(self, env): 102 """ Executes a list of Linux commands AFTER the download is finished """ 103 104 if self.attribute('post_download').value != '': 105 try: 106 env._logger.commands.write(" > " + env.replace_variables(self.attribute('post_download').value)) 107 var = getoutput(env.replace_variables(self.attribute('post_download').value)) 108 109 if env.debug: 110 print(" -> " + var) 111 except Exception as e: 112 print (" > Error executing post download : " + e ) 113 114 115 @classmethod 116 def source_systemtool(self): 117 """Returns the name of the system instalation tool for this machine.""" 118 tools = dict() 119 tools['debian']= 'apt-get' 120 tools['ubuntu']= 'apt-get' 121 tools['linuxmint']= 'apt-get' 122 tools['fedora']= 'yum' 123 tools['redhat']= 'yum' 124 tools['centos']= 'yum' 125 tools['suse']= 'yast' 126 tools['darwin']= 'port' 127 128 osName = platform.system().lower() 129 if osName.startswith('linux'): 130 (distribution, version, version_id) = distro.linux_distribution() 131 if not distribution: 132 distribution = osName 133 else: 134 distribution = distribution.lower() 135 else: 136 distribution = osName 137 138 if distribution in tools: 139 return tools[distribution] 140 else : 141 return '' 142 143 def _check_dependency_expression(self, env, valueToTest): 144 """Verifies if the preconditions exist or not.""" 145 146 # if there is no test to do, return true 147 if(not valueToTest): 148 return True 149 150 stringToChange = valueToTest 151 152 ### Clean expression 153 # elements to ignore 154 lib_var = [r'\b(or)\b', r'\b(not)\b', r'\b(and)\b',r'\b(if)\b'] 155 156 stringToChange = re.sub(r'(\(|\))',' ',stringToChange) 157 for element in lib_var : 158 stringToChange = re.sub(element,'',stringToChange) 159 160 stringToChange = re.sub(' +',' ', stringToChange) 161 162 # split the command names 163 if re.search(' ', stringToChange): 164 elementsToChange = stringToChange.split() 165 else : 166 elementsToChange = [stringToChange] 167 168 # add the call to the function that verifies if the program exist 169 elementsSet = set([]) 170 for element in elementsToChange: 171 elementsSet.add(element) 172 173 174 stringToChange = self._add_command_calls(valueToTest.replace('\\',''), 175 elementsSet) 176 177 # Evaluate if all the programs exist 178 returnValue = eval(stringToChange) 179 return returnValue 180 181 def _split_path_expression(self, inputString): 182 """Split and clean the path expression""" 183 if not inputString: 184 return set([]) 185 186 ### Clean expression 187 # elements to ignore 188 # XXX the 'not', 'and', and 'if' separators are being treated 189 # as 'or'; also, there should be some string checking for validity 190 lib_var = [r'\b(or)\b', r'\b(not)\b', r'\b(and)\b',r'\b(if)\b'] 191 192 inputString = re.sub(r'(\(|\))',' ',inputString) 193 for element in lib_var : 194 inputString = re.sub(element,'',inputString) 195 196 inputString = re.sub(' +',' ', inputString) 197 198 # split the command names 199 if re.search(' ', inputString): 200 elementsToChange = inputString.split() 201 else : 202 elementsToChange = [inputString] 203 204 # add the call to the function that verifies if the program exist 205 elementsSet = set([]) 206 for element in elementsToChange: 207 elementsSet.add(element) 208 209 return elementsSet 210 211 def _check_file_expression(self, valueToTest): 212 """Verifies if the system has the requested file or symbolic link""" 213 214 returnValue = False 215 # if there is no test to do, return true 216 if(not valueToTest): 217 return True 218 219 # The expression may contain a list of 'or' separated paths to check 220 # Split them into a set 221 elementsSet = self._split_path_expression (valueToTest) 222 223 for e in elementsSet: 224 if (os.path.isfile (e) or os.path.islink(e)): 225 returnValue = True 226 break 227 return returnValue 228 229 def _check_executable_expression(self, valueToTest): 230 """Verifies if the system has the requested executable""" 231 232 returnValue = False 233 # if there is no test to do, return true 234 if(not valueToTest): 235 return True 236 237 # The expression may contain a list of 'or' separated paths to check 238 # Split them into a set 239 elementsSet = self._split_path_expression (valueToTest) 240 241 for e in elementsSet: 242 if (os.path.exists (e) and os.access (e, os.X_OK)): 243 returnValue = True 244 break 245 return returnValue 246 247 def _check_import(self, valueToTest): 248 """Verifies if the system has the requested python import""" 249 250 try: 251 importlib.import_module(valueToTest, package=None) 252 except ImportError: 253 return False 254 return True 255 256class NoneModuleSource(ModuleSource): 257 """ This class defines an empty source, i.e. no source code fetching is 258 needed. For compatibility purposes, it is possible to create a given module 259 has no need for source code fetching. 260 """ 261 262 def __init__(self): 263 ModuleSource.__init__(self) 264 @classmethod 265 def name(cls): 266 return 'none' 267 def diff(self, env): 268 pass 269 def download(self, env): 270 pass 271 def update(self, env): 272 pass 273 def check_version(self, env): 274 return True 275 276 277class InlineModuleSource(ModuleSource): 278 """ This class enables one to create a python function, using the Bake 279 framework, to directly to search for code. 280 """ 281 282 def __init__(self): 283 ModuleSource.__init__(self) 284 285 @classmethod 286 def name(cls): 287 return 'inline' 288 289 290class BazaarModuleSource(ModuleSource): 291 """Handles the modules that have the sources stored in a bazaar repository.""" 292 293 def __init__(self): 294 """ Specific attributes definition.""" 295 296 ModuleSource.__init__(self) 297 self.add_attribute('url', '', 'The url to clone from', 298 mandatory=True) 299 self.add_attribute('revision', None, 'The revision to update to after' 300 ' the clone.') 301 @classmethod 302 def name(cls): 303 """ Identifier of the type of the tool used.""" 304 return 'bazaar' 305 306 def diff(self, env): 307 pass 308 309 def download(self, env): 310 """ Downloads the code, of a specific version, using Bazaar.""" 311 312 rev_arg = [] 313 if not self.attribute('revision').value is None: 314 rev_arg.extend(['-r', self.attribute('revision').value]) 315 env.run(['bzr', 'branch'] + rev_arg + [self.attribute('url').value, 316 env.srcdir]) 317 318 def update(self, env): 319 """ Updates the code using a specific version from the repository.""" 320 321 rev_arg = [] 322 if not self.attribute('revision').value is None: 323 rev_arg.extend(['-r', self.attribute('revision').value]) 324 env.run(['bzr', 'pull'] + rev_arg + [self.attribute('url').value], 325 directory=env.srcdir) 326 327 def check_version(self, env): 328 """ Checks if the tool is available and with the needed version.""" 329 return env.check_program('bzr', version_arg='--version', 330 version_regexp='(\d+)\.(\d+)', 331 version_required=(2, 1)) 332 333 334class MercurialModuleSource(ModuleSource): 335 """Handles the modules that have the sources stored in a mercurial 336 repository. 337 """ 338 339 def __init__(self): 340 """ Specific attributes definition.""" 341 342 ModuleSource.__init__(self) 343 self.add_attribute('url', '', 'The url to clone from', 344 mandatory=True) 345 self.add_attribute('revision', 'tip', 'The revision to update to ' 346 'after the clone. ' 347 'If no value is specified, the default is "tip"') 348 @classmethod 349 def name(cls): 350 """ Identifier of the type of the tool used.""" 351 return 'mercurial' 352 353 def download(self, env): 354 """ Downloads the code, of a specific version, using Mercurial.""" 355 356 env.run(['hg', 'clone', '-U', self.attribute('url').value, env.srcdir]) 357 env.run(['hg', 'update', '-r', self.attribute('revision').value], 358 directory=env.srcdir) 359 360 def update(self, env): 361 """ Updates the code using a specific version from the repository.""" 362 363 env.run(['hg', 'pull', self.attribute('url').value], 364 directory=env.srcdir) 365 env.run(['hg', 'update', '-r', self.attribute('revision').value], 366 directory=env.srcdir) 367 368 def check_version(self, env): 369 """ Checks if the tool is available and with the needed version.""" 370 return env.check_program('hg') 371 372 373import shutil 374class ArchiveModuleSource(ModuleSource): 375 """Handles the modules that have the sources as a single tarball like file.""" 376 377 def __init__(self): 378 """ Specific attributes definition.""" 379 380 ModuleSource.__init__(self) 381 self.add_attribute('url', None, 'The url to clone from', 382 mandatory=True) 383 self.add_attribute('additional-module', None, 384 "Tags this module as an additional sub-module to be" 385 " added to another module.", 386 mandatory=False) 387 self.add_attribute('extract_directory', None, 388 "The name of the directory the source code will " 389 "be extracted to naturally. If no value is " 390 "specified, directory is assumed to be equal to " 391 "the archive without the file extension.") 392 @classmethod 393 def name(cls): 394 """ Identifier of the type of the tool used.""" 395 return 'archive' 396 397 def _decompress(self, filename, env): 398 """Uses the appropriate tool to uncompress the sources.""" 399 400 import tempfile 401 import os 402 tempdir = tempfile.mkdtemp(dir=env.srcrepo) 403 extensions = [ 404 ['tar', ['tar', 'xf']], 405 ['tar.gz', ['tar', 'zxf']], 406 ['tar.Z', ['tar', 'zxf']], 407 ['tar.bz2', ['tar', 'jxf']], 408 ['zip', ['unzip']], 409 ['rar', ['unrar', 'e']], 410 ['tar.xz', ['tar', 'Jxf']], 411 ['xz', ['unxz']], 412 ['7z', ['7z', 'x']], 413 ['tgz', ['tar', 'xzvf']], 414 ['tbz2', ['tar', 'jxf']] 415 ] 416 417 # finds the right tool 418 for extension, command in extensions: 419 if filename.endswith(extension): 420 env.run(command + [filename], directory=tempdir) 421 if self.attribute('extract_directory').value is not None: 422 actual_extract_dir = self.attribute('extract_directory').value 423 else: 424 actual_extract_dir = os.path.basename(filename)[0:-len(extension) - 1] 425 426 # finally, rename the extraction directory to the target 427 # directory name. 428 try: 429 destDir=os.path.join(tempdir, actual_extract_dir) 430 431 if os.path.isdir(env.srcdir): 432 bake.Utils.mergeDirs(destDir, env.srcdir) 433 else: 434 os.rename(destDir, env.srcdir) 435 436 shutil.rmtree(tempdir) # delete directory 437 except (OSError, IOError) as e: 438 raise TaskError("Rename problem for module: %s, from: %s, " 439 "to: %s, Error: %s" 440 % (env._module_name,os.path.join(tempdir, 441 actual_extract_dir), 442 env.srcdir, e)) 443 return 444 raise TaskError('Unknown Archive Type: %s, for module: %s' % 445 (filename, env._module_name)) 446 447 def download(self, env): 448 """Downloads the specific file.""" 449 450 451 452 url_local = self.attribute('url').value 453 454 filename = os.path.basename(urlparse(url_local).path) 455 tmpfile = os.path.join(env.srcrepo, filename) 456 try: 457 urlretrieve(url_local, filename=tmpfile) 458 except IOError as e: 459 raise TaskError('Download problem for module: %s, URL: %s, Error: %s' 460 % (env._module_name,self.attribute('url').value, e)) 461 462 self._decompress(tmpfile, env) 463 464 def update(self, env): 465 """ Empty, no update is possible for files.""" 466 pass 467 468 def check_version(self, env): 469 470 """Verifies if the right program exists in the system to handle the 471 given compressed source file. 472 """ 473 extensions = [ 474 ['tar', 'tar'], 475 ['tar.gz', 'tar'], 476 ['tar.Z', 'tar'], 477 ['tar.bz2', 'tar'], 478 ['tgz', 'tar'], 479 ['zip', 'unzip'], 480 ['rar', 'unrar'], 481 ['7z', '7z'], 482 ['xz', 'unxz'], 483 ['Z', 'uncompress'] 484 ] 485 try: 486 filename = os.path.basename(urlparse(self.attribute('url').value).path) 487 except AttributeError as e: 488 return False 489 490 for extension, program in extensions: 491 if filename.endswith(extension): 492 return env.check_program(program) 493 return False 494 495 496class SystemDependency(ModuleSource): 497 """Handles the system dependencies for a given module. If a system 498 dependency is not met, advise the user on how to install the 499 missing dependency, if possible. Dependencies may be expressed 500 by requesting bake to check for a specific file (such as a library 501 or header file) in one or more locations, or checking for an 502 executable program in one or more locations. 503 """ 504 505 def __init__(self): 506 """ Specific attributes definition.""" 507 508 ModuleSource.__init__(self) 509 self.add_attribute('dependency_test', None, 510 '(DEPRECATED) The name of the dependency', 511 mandatory=False) 512 self.add_attribute('file_test', None, 'System file to try to locate', 513 mandatory=False) 514 self.add_attribute('executable_test', None, 'Executable to try to locate', 515 mandatory=False) 516 self.add_attribute('import_test', None, 'Python import to try', 517 mandatory=False) 518 self.add_attribute('try_to_install', 'false', 519 '(DEPRECATED) If should try to install or not', 520 mandatory=False) 521 self.add_attribute('sudoer_install', None, 522 '(DEPRECATED) Try to install the dependency as sudoer', 523 mandatory=False) 524 self.add_attribute('name_yum', None, 525 'The package name of the module, for RPMs', 526 mandatory=False) 527 self.add_attribute('name_apt-get', None, 528 'The package name of the module, for use with apt-get ', 529 mandatory=False) 530 self.add_attribute('name_yast', None, 531 'The package name of the module, for use with yast', 532 mandatory=False) 533 self.add_attribute('name_port', None, 534 'The package name of the module, for use with port (Mac OS)', 535 mandatory=False) 536 self.add_attribute('more_information', None, 537 'Gives users a better hint of where to search for the module' , 538 mandatory=True) 539 @classmethod 540 def name(cls): 541 """ Identifier of the type of the tool used.""" 542 return 'system_dependency' 543 544 def _get_command(self, distribution): 545 """Finds the proper installer command line, given the OS distribution. 546 """ 547 548 distributions = [ 549 ['debian', 'apt-get install '], 550 ['ubuntu', 'apt-get install '], 551 ['linuxmint', 'apt-get install '], 552 ['fedora', 'yum install '], 553 ['redhat', 'yum install '], 554 ['centos', 'yum install '], 555 ['suse', 'yast --install '], 556 ['darwin', 'port install '], 557 ] 558 559 for name, command in distributions: 560 if distribution.startswith(name): 561 return command 562 return '' 563 564 def remove(self, env): 565 """ Removes the the present version of the dependency.""" 566 567 # if the download is dependent of the machine's architecture 568 osName = platform.system().lower() 569 if(not osName.startswith('linux') and not osName.startswith('darwin')): 570 raise TaskError("Not a Linux/Mac OS machine, self installation is not" 571 " possible in %s for module: %s, %s" % 572 (osName, env._module_name, 573 self.attribute('error_message').value)) 574 575 (distribution, version, version_id) = distro.linux_distribution() 576 577 if not distribution: 578 distribution = 'darwin' # osName 579 else: 580 distribution = distribution.lower() 581 582 command = self._get_command(distribution) 583 command = command.rstrip().rsplit(' ', 1)[0] + ' remove' 584 installerName = self.attribute('name_' + command.split()[0]).value 585 586 # if didn't find the specific installer name uses the default one 587 if(not installerName): 588 installerName = env._module_name 589 590 # if should try to remove as sudoer 591 sudoer=self.attribute('sudoer_install').value 592 if sudoer: sudoer = sudoer.lower() 593 if(sudoer =='true' and (not env.sudoEnabled)): 594 raise TaskError(' Module: \"%s\" requires sudo rights, ask your' 595 ' system admin to remove \"%s\" from your machine.\n' 596 ' More information from the module: \"%s\"' 597 % (env._module_name, installerName, 598 self.attribute('more_information').value)) 599 600 if(env.sudoEnabled): 601 command = "sudo "+ command 602 command = command 603 604 # uses apt-get/yum/... to remove the module 605 try: 606 env.run((command + " " + installerName).split(" "), 607 directory=env.srcrepo) 608 except IOError as e: 609 raise TaskError(' Removing module problem: \"%s\", Message: %s, Error: %s' 610 % (env._module_name, 611 self.attribute('more_information').value, e)) 612 except TaskError as e1: 613 if(env.sudoEnabled): 614 e1.reason = (" Removing problem for module: \"%s\", " 615 "\n Probably either you miss rights or the module is" 616 " not present on your package management databases." 617 "\n Try to either talk to your system admin or review your " 618 "library database to add \"%s\"\n" 619 " More information from the module: \"%s\"" 620 % (env._module_name, installerName, 621 self.attribute('more_information').value)) 622 else: 623 e1.reason = (" Removing problem for module: \"%s\", " 624 "\n Probably you either need super user rights" 625 " to remove the packet, or the module is" 626 " not present on your package management databases." 627 "\n Try calling bake with the --sudo option and/or " 628 "review your library database to add \"%s\"\n" 629 " More information from the module: \"%s\"" 630 % (env._module_name, installerName, 631 self.attribute('more_information').value)) 632 633 raise TaskError(" Removing module problem: \"%s\",\n Probably you" 634 "miss sudo rights or the module is not present on your package " 635 "management databases. \n Try calling bake with --sudo or reviewing your" 636 " library database to add \"%s\"\n" 637 " More information from the module: \"%s\"" 638 % (env._module_name, installerName, 639 self.attribute('more_information').value)) 640 return True 641 642 def _add_command_calls(self, stringToChange, elements): 643 """ Define the command calls to be executed. """ 644 645 for element in elements: 646 stringToChange= re.sub(element + "(\s|\)|$)" , 647 'env.check_program(\'' + element.replace('\\','') + 648 '\')\\1', stringToChange) 649 return stringToChange 650 651 652 653 def download(self, env): 654 """ Verifies if the system dependency exists, if exists returns true, 655 if not, and we are in a supported machine, tries to download and install 656 the dependency. 657 """ 658 659 selfInstalation = self.attribute('try_to_install').value 660 if selfInstalation: selfInstalation = selfInstalation.lower() 661 662 if not selfInstalation == 'true' : 663 raise TaskError(' Module: \"%s\" is required by other modules but it is not available on your system.\n' 664 ' Ask your system admin or review your library database to add \"%s\"\n' 665 ' More information from the module: \"%s\"' 666 % (env._module_name, env._module_name, 667 self.attribute('more_information').value)) 668 669 # even if should try to install, if it is not a supported machine 670 # we will not be able to 671 osName = platform.system().lower().strip() 672 if((osName.startswith('linux') or osName.startswith('darwin')) and 673 selfInstalation == 'true'): 674 (distribution, version, version_id) = distro.linux_distribution() 675 676 if not distribution: 677 distribution = osName.split()[0] # osName 678 else: 679 distribution = distribution.lower() 680 681 command = self._get_command(distribution) 682 683 # didn't recognize the distribution, asks user to install by himself 684 if command == '' : 685 raise TaskError(' Module: \"%s\" is required by other modules but it is not available on your system.\n' 686 ' Ask your system admin\n' 687 ' > More information from the module: \"%s\"' 688 % (env._module_name, 689 self.attribute('more_information').value)) 690 691 installerName = self.attribute('name_' + command.split()[0]).value 692 693 # if didn't find the specific installer name uses the default one 694 if(not installerName): 695 installerName = env._module_name 696 697 if(not command): 698 selfInstalation = 'false' 699 700 else : 701 selfInstalation = 'false' 702 703 if not env._sudoEnabled : 704 raise TaskError(' Module: \"%s\" is required by other modules and is not available on your system.\n' 705 ' Ask your system admin to install it.\n' 706 ' > More information from the module: \"%s\"' 707 % (env._module_name, 708 self.attribute('more_information').value)) 709 710 711 errorTmp = None 712 sudoer=self.attribute('sudoer_install').value 713 if selfInstalation=='true': 714 # Try to install if possible 715 716 # if should try to install as sudoer 717 if sudoer: sudoer = sudoer.lower() 718 if(sudoer=='true' and (not env.sudoEnabled)): 719 raise TaskError(' Module: \"%s\" requires sudo rights, if' 720 ' you have the right, call bake with the' 721 ' --sudo option, or ask your system admin' 722 ' to install \"%s\" in your machine.\n' 723 ' > More information from the module: \"%s\"' 724 % (env._module_name, installerName, 725 self.attribute('more_information').value)) 726 727 # if the user asked to install everything as sudoer... do it! 728 if(env.sudoEnabled): 729 command = "sudo "+ command 730 command = command 731 732 try: 733 env.run((command + installerName).split(" "), 734 directory=env.srcrepo) 735 return True 736 except IOError as e: 737 errorTmp = (' Self installation problem for module: \"%s\", ' 738 'Error: %s' % (env._module_name, e)) 739 except TaskError as e1: 740 if(env.sudoEnabled): 741 e1.reason = (" Self installation problem for module: \"%s\", " 742 "\n Probably either you miss sudo rights or the module is" 743 " not present on your package management databases." 744 "\n Try to either talk to your system admin or review your " 745 "library database to add \"%s\"\n" 746 " > More information from the module: \"%s\"" 747 % (env._module_name, installerName, 748 self.attribute('more_information').value)) 749 else: 750 e1.reason = (" Self installation problem for module: \"%s\", " 751 "\n Probably either you need super user rights to install the packet," 752 "or that the module is" 753 " not present on your package management databases." 754 "\n Try calling bake with the --sudo option and/or " 755 "review your library database to add \"%s\"\n" 756 " > More information from the module: \"%s\"" 757 % (env._module_name, installerName, 758 self.attribute('more_information').value)) 759 raise e1 760 761 return True 762 763 def update(self, env): 764 """Empty, no Update available for system dependency. """ 765 pass 766 767 def build(self, env): 768 """ Empty, no build is possible for system dependencies.""" 769 pass 770 771 def check_version(self, env): 772 """Verifies if the right program exists in the system to handle 773 the given compressed source file. 774 """ 775# 776# distributions = [ 777# ['debian', 'apt-get'], 778# ['ubuntu', 'apt-get'], 779# ['fedora', 'yum'], 780# ['redhat', 'yum'], 781# ['centos', 'yum'], 782# ['suse', 'yast'], 783# ['darwin', 'port'], 784# ] 785# 786 787# (distribution, version, version_id) = distro.linux_distribution() 788# if not distribution: 789# distribution = 'darwin' # osName 790# else: 791# distribution = distribution.lower() 792 793 program = self.source_systemtool() 794 795 if not program == '': 796 return env.check_program(program) 797# for dist, program in distributions: 798# if distribution.startswith(dist): 799# return env.check_program(program) 800 return False 801 802 803class CvsModuleSource(ModuleSource): 804 """Handles the modules that have the sources stored in a CVS repository.""" 805 806 def __init__(self): 807 """ Specific attributes definition.""" 808 809 ModuleSource.__init__(self) 810 self.add_attribute('root', '', 811 'Repository root specification to checkout from.', 812 mandatory=True) 813 self.add_attribute('module', '', 'Module to checkout.', mandatory=True) 814 self.add_attribute('checkout_directory', None, "Name of directory " 815 "checkout defaults to. If unspecified, defaults" 816 " to the name of the module being checked out.") 817 self.add_attribute('date', None, 'Date to checkout') 818 819 @classmethod 820 def name(cls): 821 """ Identifier of the type of the tool used.""" 822 823 return 'cvs' 824 825 def download(self, env): 826 """ Downloads the last CVS code, or from a specific date. """ 827 828 import tempfile 829 try: 830 tempdir = tempfile.mkdtemp(dir=env.srcrepo) 831 except OSError as e: 832 raise TaskError('Could not create temporary directory %s, Error: %s' 833 % (env.srcrepo, e)) 834 835 env.run(['cvs', '-d', self.attribute('root').value, 'login'], 836 directory=tempdir) 837 838 checkout_options = [] 839 if not self.attribute('date').value is None: 840 checkout_options.extend(['-D', self.attribute('date').value]) 841 env.run(['cvs', '-d', self.attribute('root').value, 'checkout'] + 842 checkout_options + [self.attribute('module').value], 843 directory=tempdir) 844 845 if self.attribute('checkout_directory').value is not None: 846 actual_checkout_dir = self.attribute('checkout_directory').value 847 else: 848 actual_checkout_dir = self.attribute('module').value 849 850 import os 851 import shutil 852 try: 853 os.rename(os.path.join(tempdir, actual_checkout_dir), env.srcdir) 854 shutil.rmtree(tempdir) 855 except AttributeError as e: 856 raise TaskError('Atribute type error expected String, Error: %s' 857 % e) 858 859 860 def update(self, env): 861 """Updates the code for the date specified, or for the today's code. """ 862 863 # just update does not work, it has to give a date for the update 864 # either a date is provided, or takes today as date 865 update_options = [] 866 if not self.attribute('date').value is None: 867 update_options.extend(['-D', self.attribute('date').value]) 868 else: 869 update_options.extend(['-D', str(date.today())]) 870 871 env.run(['cvs', 'up'] + update_options, directory=env.srcdir) 872 873 def check_version(self, env): 874 """ Checks if the tool is available and with the needed version.""" 875 876 return env.check_program('cvs') 877 878 879class GitModuleSource(ModuleSource): 880 """Handles the modules that have the sources stored in a git repository.""" 881 882 def __init__(self): 883 ModuleSource.__init__(self) 884 self.add_attribute('url', '', 'Url to clone the source tree from.', 885 mandatory=True) 886 self.add_attribute('revision', '', 887 "Revision to checkout. Defaults to origin/master" 888 " reference.") 889 self.add_attribute('branch', '', 'Branch to checkout.') 890 self.add_attribute('fetch_option', '', 'Options to add git fetch command.') 891 @classmethod 892 def name(cls): 893 """ Identifier of the type of the tool used.""" 894 895 return 'git' 896 897 def download(self, env): 898 import tempfile 899 import os 900 try: 901 tempdir = tempfile.mkdtemp(dir=env.srcrepo) 902 except AttributeError as e1: 903 raise TaskError('Attribute type error, expected String, Error: %s' % e1) 904 except OSError as e2: 905 raise TaskError('Could not create temporary file, Error: %s' % e2) 906 907 checkedOut = False 908 909 env.run(['git', 'init'], directory=tempdir) 910 env.run(['git', 'remote', 'add', 'origin', self.attribute('url').value], 911 directory=tempdir) 912 if self.attribute('fetch_option').value != '': 913 env.run(['git', 'fetch', self.attribute('fetch_option').value], 914 directory=tempdir) 915 else: 916 env.run(['git', 'fetch'], directory=tempdir) 917 918 if self.attribute('branch').value != '': 919 env.run(['git', 'checkout', self.attribute('branch').value], 920 directory=tempdir) 921 checkedOut = True 922 923 if self.attribute('revision').value != '': 924 env.run(['git', 'checkout', self.attribute('revision').value], 925 directory=tempdir) 926 checkedOut = True 927 928 if not checkedOut: 929 env.run(['git', 'pull', 'origin', 'master'], directory=tempdir) 930 931 os.rename(tempdir, env.srcdir) 932 933 def update(self, env): 934 """ Updates the code using a specific version from the repository.""" 935 936 env.run(['git', 'stash'], directory=env.srcdir) 937 env.run(['git', 'rebase', self.attribute('revision').value], directory=env.srcdir) 938 try: 939 env.run(['git', 'stash','pop'], directory=env.srcdir) 940 except TaskError as t: 941 if not ' 1' in t.reason: 942 raise t 943 env._logger.commands.write(' No perceived changes on the local repository.\n') 944 945 946# env.run(['git', 'fetch'], directory=env.srcdir) 947# env.run(['git', 'checkout', self.attribute('revision').value], 948# directory=env.srcdir) 949 950 def check_version(self, env): 951 """ Checks if the tool is available and with the needed version.""" 952 return env.check_program('git') 953