|
@@ -0,0 +1,976 @@
|
|
|
+#! /usr/bin/env python
|
|
|
+# -*- coding: UTF-8 -*-
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# Releases *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# 1.0: march 18th, 2015
|
|
|
+# first release
|
|
|
+# 2.0: october 2th, 2015
|
|
|
+# added several target definition for rules
|
|
|
+# 2.1: october 5th, 2015
|
|
|
+# added checking routine formal argument run-time types
|
|
|
+# 2.2: october 24th, 2015
|
|
|
+# changed subprocess.Popen to subprocess.call in runCommand
|
|
|
+# added command tool checking using 'find_executable' function
|
|
|
+# added optional argument to Make class initializer to log command utility tool path
|
|
|
+# 2.3: april 16th, 2016
|
|
|
+# added advance percentage
|
|
|
+# 3.0: may 30th, 2016
|
|
|
+# compatibility with Python 3:
|
|
|
+# print xyz ---> print (xyz)
|
|
|
+# change isinstance function arguments ---> function argumentIsString
|
|
|
+# subprocess.call: add "universal_newlines=True" argument
|
|
|
+# added test (job.mReturnCode != None) lines 727 and 739
|
|
|
+# 3.1: may 26th, 2018
|
|
|
+# Added tolerance in secondary dependency file syntax:
|
|
|
+#
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# http://www.diveintopython3.net/porting-code-to-python-3-with-2to3.html
|
|
|
+
|
|
|
+import subprocess, sys, os, copy
|
|
|
+import urllib, shutil, subprocess
|
|
|
+import platform, json, operator
|
|
|
+import threading, types, traceback
|
|
|
+
|
|
|
+if sys.version_info >= (2, 6) :
|
|
|
+ import multiprocessing
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# find_executable *
|
|
|
+# From: *
|
|
|
+# https://gist.github.com/4368898 *
|
|
|
+# Public domain code by anatoly techtonik <techtonik@gmail.com> *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def find_executable(executable, path=None):
|
|
|
+ """Try to find 'executable' in the directories listed in 'path' (a
|
|
|
+ string listing directories separated by 'os.pathsep'; defaults to
|
|
|
+ os.environ['PATH']). Returns the complete filename or None if not
|
|
|
+ found
|
|
|
+ """
|
|
|
+ if path is None:
|
|
|
+ path = os.environ['PATH']
|
|
|
+ paths = path.split(os.pathsep)
|
|
|
+ extlist = ['']
|
|
|
+ if os.name == 'os2':
|
|
|
+ (base, ext) = os.path.splitext(executable)
|
|
|
+ # executable files on OS/2 can have an arbitrary extension, but
|
|
|
+ # .exe is automatically appended if no dot is present in the name
|
|
|
+ if not ext:
|
|
|
+ executable = executable + ".exe"
|
|
|
+ elif sys.platform == 'win32':
|
|
|
+ pathext = os.environ['PATHEXT'].lower().split(os.pathsep)
|
|
|
+ (base, ext) = os.path.splitext(executable)
|
|
|
+ if ext.lower() not in pathext:
|
|
|
+ extlist = pathext
|
|
|
+ for ext in extlist:
|
|
|
+ execname = executable + ext
|
|
|
+ if os.path.isfile(execname):
|
|
|
+ return execname
|
|
|
+ else:
|
|
|
+ for p in paths:
|
|
|
+ f = os.path.join(p, execname)
|
|
|
+ if os.path.isfile(f):
|
|
|
+ return f
|
|
|
+ else:
|
|
|
+ return None
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# processorCount *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def processorCount () :
|
|
|
+ if sys.version_info >= (2, 6) :
|
|
|
+ coreCount = multiprocessing.cpu_count ()
|
|
|
+ else:
|
|
|
+ coreCount = 1
|
|
|
+ return coreCount
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# argumentIsString *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def argumentIsString (argument) :
|
|
|
+ if sys.version_info < (3,0):
|
|
|
+ return isinstance (argument, types.StringTypes)
|
|
|
+ else:
|
|
|
+ return type (argument) is str
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# FOR PRINTING IN COLOR *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def BLACK () :
|
|
|
+ return '\033[90m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def RED () :
|
|
|
+ return '\033[91m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def GREEN () :
|
|
|
+ return '\033[92m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def YELLOW () :
|
|
|
+ return '\033[93m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def BLUE () :
|
|
|
+ return '\033[94m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def MAGENTA () :
|
|
|
+ return '\033[95m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def CYAN () :
|
|
|
+ return '\033[96m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def WHITE () :
|
|
|
+ return '\033[97m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def ENDC () :
|
|
|
+ return '\033[0m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def BOLD () :
|
|
|
+ return '\033[1m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def UNDERLINE () :
|
|
|
+ return '\033[4m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def BLINK () :
|
|
|
+ return '\033[5m'
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def BOLD_BLUE () :
|
|
|
+ return BOLD () + BLUE ()
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def BOLD_GREEN () :
|
|
|
+ return BOLD () + GREEN ()
|
|
|
+
|
|
|
+#······················································································································*
|
|
|
+
|
|
|
+def BOLD_RED () :
|
|
|
+ return BOLD () + RED ()
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# runHiddenCommand *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def runHiddenCommand (cmd, logUtilityTool=False) :
|
|
|
+ executable = find_executable (cmd [0])
|
|
|
+ if executable == None:
|
|
|
+ print (BOLD_RED () + "*** Cannot find '" + cmd[0] + "' executable ***" + ENDC ())
|
|
|
+ sys.exit (1)
|
|
|
+ if logUtilityTool:
|
|
|
+ print ("Utility tool is '" + executable + "'")
|
|
|
+ result = ""
|
|
|
+ childProcess = subprocess.Popen (cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
|
|
|
+ while True:
|
|
|
+ line = childProcess.stdout.readline ()
|
|
|
+ if line != "":
|
|
|
+ result += line
|
|
|
+ else:
|
|
|
+ childProcess.wait ()
|
|
|
+ if childProcess.returncode != 0 :
|
|
|
+ sys.exit (childProcess.returncode)
|
|
|
+ return result
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# runCommand *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def runCommand (cmd, title, showCommand, logUtilityTool) :
|
|
|
+ if title != "":
|
|
|
+ print (BOLD_BLUE () + title + ENDC ())
|
|
|
+ if (title == "") or showCommand :
|
|
|
+ cmdAsString = ""
|
|
|
+ for s in cmd:
|
|
|
+ if (s == "") or (s.find (" ") >= 0):
|
|
|
+ cmdAsString += '"' + s + '" '
|
|
|
+ else:
|
|
|
+ cmdAsString += s + ' '
|
|
|
+ print (cmdAsString)
|
|
|
+ executable = find_executable (cmd [0])
|
|
|
+ if executable == None:
|
|
|
+ print (BOLD_RED () + "*** Cannot find '" + cmd[0] + "' executable ***" + ENDC ())
|
|
|
+ sys.exit (1)
|
|
|
+ if logUtilityTool:
|
|
|
+ print ("Utility tool is '" + executable + "'")
|
|
|
+ returncode = subprocess.call (cmd)
|
|
|
+ if returncode != 0 :
|
|
|
+ sys.exit (returncode)
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# runInThread *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def runInThread (job, displayLock, terminationSemaphore):
|
|
|
+ executable = find_executable (job.mCommand [0])
|
|
|
+ if executable == None:
|
|
|
+ print (BOLD_RED () + "*** Cannot find '" + job.mCommand[0] + "' executable ***" + ENDC ())
|
|
|
+ job.mReturnCode = 1
|
|
|
+ terminationSemaphore.release ()
|
|
|
+ else:
|
|
|
+ if job.mLogUtilityTool :
|
|
|
+ print ("Utility tool is '" + executable + "'")
|
|
|
+ childProcess = subprocess.Popen (job.mCommand, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
|
|
|
+ while True:
|
|
|
+ line = childProcess.stdout.readline ()
|
|
|
+ if line != "":
|
|
|
+ job.mOutputLines.append (line)
|
|
|
+ displayLock.acquire ()
|
|
|
+ sys.stdout.write (line) # Print without newline
|
|
|
+ displayLock.release ()
|
|
|
+ else:
|
|
|
+ childProcess.wait ()
|
|
|
+ job.mReturnCode = childProcess.returncode
|
|
|
+ terminationSemaphore.release ()
|
|
|
+ break
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# modificationDateForFile *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+def modificationDateForFile (dateCacheDictionary, file):
|
|
|
+ absFilePath = os.path.abspath (file)
|
|
|
+ if absFilePath in dateCacheDictionary :
|
|
|
+ return dateCacheDictionary [absFilePath]
|
|
|
+ elif not os.path.exists (absFilePath):
|
|
|
+ date = sys.float_info.max # Very far in future
|
|
|
+ dateCacheDictionary [absFilePath] = date
|
|
|
+ return date
|
|
|
+ else:
|
|
|
+ date = os.path.getmtime (absFilePath)
|
|
|
+ dateCacheDictionary [absFilePath] = date
|
|
|
+ return date
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# class PostCommand *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+class PostCommand:
|
|
|
+ mCommand = []
|
|
|
+ mTitle = ""
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def __init__ (self, title = ""):
|
|
|
+ self.mCommand = []
|
|
|
+ self.mTitle = title
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# class Job *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+class Job:
|
|
|
+ mTargets = []
|
|
|
+ mCommand = []
|
|
|
+ mTitle = ""
|
|
|
+ mRequiredFiles = []
|
|
|
+ mPostCommands = []
|
|
|
+ mReturnCode = None
|
|
|
+ mPriority = 0
|
|
|
+ mState = 0 # 0: waiting for execution
|
|
|
+ mOutputLines = []
|
|
|
+ mOpenSourceOnError = False # Do not try to open source file on error
|
|
|
+ mLogUtilityTool = False
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def __init__ (self, targets, requiredFiles, command, postCommands, priority, title, openSourceOnError, logUtilityTool):
|
|
|
+ self.mTargets = copy.deepcopy (targets)
|
|
|
+ self.mCommand = copy.deepcopy (command)
|
|
|
+ self.mRequiredFiles = copy.deepcopy (requiredFiles)
|
|
|
+ self.mTitle = copy.deepcopy (title)
|
|
|
+ self.mPostCommands = copy.deepcopy (postCommands)
|
|
|
+ self.mPriority = priority
|
|
|
+ self.mOutputLines = []
|
|
|
+ self.mOpenSourceOnError = openSourceOnError
|
|
|
+ self.mLogUtilityTool = logUtilityTool
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def run (self, displayLock, terminationSemaphore, showCommand, progressString):
|
|
|
+ displayLock.acquire ()
|
|
|
+ if self.mTitle != "":
|
|
|
+ print (progressString + BOLD_BLUE () + self.mTitle + ENDC ())
|
|
|
+ if (self.mTitle == "") or showCommand :
|
|
|
+ cmdAsString = ""
|
|
|
+ for s in self.mCommand:
|
|
|
+ if (s == "") or (s.find (" ") >= 0):
|
|
|
+ cmdAsString += '"' + s + '" '
|
|
|
+ else:
|
|
|
+ cmdAsString += s + ' '
|
|
|
+ print (progressString + cmdAsString)
|
|
|
+ displayLock.release ()
|
|
|
+ thread = threading.Thread (target=runInThread, args=(self, displayLock, terminationSemaphore))
|
|
|
+ thread.start()
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def runPostCommand (self, displayLock, terminationSemaphore, showCommand):
|
|
|
+ postCommand = self.mPostCommands [0]
|
|
|
+ self.mCommand = postCommand.mCommand
|
|
|
+ displayLock.acquire ()
|
|
|
+ print (BOLD_BLUE () + postCommand.mTitle + ENDC ())
|
|
|
+ if showCommand:
|
|
|
+ cmdAsString = ""
|
|
|
+ for s in self.mCommand:
|
|
|
+ if (s == "") or (s.find (" ") >= 0):
|
|
|
+ cmdAsString += '"' + s + '" '
|
|
|
+ else:
|
|
|
+ cmdAsString += s + ' '
|
|
|
+ print (cmdAsString)
|
|
|
+ displayLock.release ()
|
|
|
+ thread = threading.Thread (target=runInThread, args=(self, displayLock, terminationSemaphore))
|
|
|
+ thread.start()
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# class Rule *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+class Rule:
|
|
|
+ mTargets = []
|
|
|
+ mDependences = []
|
|
|
+ mCommand = []
|
|
|
+ mSecondaryMostRecentModificationDate = 0.0 # Far in the past
|
|
|
+ mTitle = ""
|
|
|
+ mPostCommands = []
|
|
|
+ mPriority = 0
|
|
|
+ mDeleteTargetOnError = False # No operation on error
|
|
|
+ mCleanOperation = 0 # No operation on clean
|
|
|
+ mOpenSourceOnError = False # Do not try to open source file on error
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def __init__ (self, targets, title = ""):
|
|
|
+ # print ("Rule '" + title + "'")
|
|
|
+ if not type (targets) is list:
|
|
|
+ print (BOLD_RED () + "*** Rule type instanciation: first argument 'targets' is not a list ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ else:
|
|
|
+ for aTarget in targets:
|
|
|
+ # print (" Target '" + aTarget + "'")
|
|
|
+ if not argumentIsString (aTarget):
|
|
|
+ print (BOLD_RED () + "*** Rule type instanciation: an element of first argument 'targets' is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if not argumentIsString (title):
|
|
|
+ print (BOLD_RED () + "*** Rule type instanciation: second argument 'title' is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ self.mTargets = copy.deepcopy (targets)
|
|
|
+ self.mDependences = []
|
|
|
+ self.mCommand = []
|
|
|
+ self.mSecondaryMostRecentModificationDate = 0.0
|
|
|
+ self.mPostCommands = []
|
|
|
+ self.mPriority = 0
|
|
|
+ self.mDeleteTargetOnError = False # No operation on error
|
|
|
+ self.mOpenSourceOnError = False # Do not try to open source file on error
|
|
|
+ self.mCleanOperation = 0 # No operation on clean
|
|
|
+ if title == "":
|
|
|
+ self.mTitle = "Building"
|
|
|
+ for s in targets:
|
|
|
+ self.mTitle += " " + s
|
|
|
+ else:
|
|
|
+ self.mTitle = copy.deepcopy (title)
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def deleteTargetFileOnClean (self):
|
|
|
+ self.mCleanOperation = 1
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def deleteTargetDirectoryOnClean (self):
|
|
|
+ self.mCleanOperation = 2
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def enterSecondaryDependanceFile (self, secondaryDependanceFile, make):
|
|
|
+ if not argumentIsString (secondaryDependanceFile):
|
|
|
+ print (BOLD_RED () + "*** Rule.enterSecondaryDependanceFile: 'secondaryDependanceFile' argument is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if make.mSelectedGoal != "clean":
|
|
|
+ filePath = os.path.abspath (secondaryDependanceFile)
|
|
|
+ if not os.path.exists (filePath):
|
|
|
+ self.mSecondaryMostRecentModificationDate = sys.float_info.max # Very far in future
|
|
|
+ else:
|
|
|
+ f = open (filePath, "r")
|
|
|
+ s = f.read ()
|
|
|
+ f.close ()
|
|
|
+ s = s.replace ("\\ ", "\x01") # Replace escaped spaces by \0x01
|
|
|
+ s = s.replace ("\\\n", "") # Suppress \ at the end of lines
|
|
|
+ liste = s.split ("\n\n")
|
|
|
+ # print ("DEP " + secondaryDependanceFile)
|
|
|
+ for s in liste:
|
|
|
+ # print ("S " + s)
|
|
|
+ components = s.split (':')
|
|
|
+ # print (str (len (components)))
|
|
|
+ #target = components [0].replace ("\x01", " ")
|
|
|
+ #print ("------- Optional dependency rules for target '" + target + "'")
|
|
|
+ #print ("Secondary target '" + target + "'")
|
|
|
+ if len (components) > 1 :
|
|
|
+ for src in components [1].split ():
|
|
|
+ secondarySource = src.replace ("\x01", " ")
|
|
|
+ # print (" SECONDARY SOURCE '" + secondarySource + "'")
|
|
|
+ modifDate = modificationDateForFile (make.mModificationDateDictionary, secondarySource)
|
|
|
+ if self.mSecondaryMostRecentModificationDate < modifDate :
|
|
|
+ self.mSecondaryMostRecentModificationDate = modifDate
|
|
|
+ #print (BOLD_BLUE () + str (modifDate) + ENDC ())
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+# class Make *
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|
|
|
+
|
|
|
+class Make:
|
|
|
+ mRuleList = []
|
|
|
+ mJobList = []
|
|
|
+ mErrorCount = 0
|
|
|
+ mModificationDateDictionary = {}
|
|
|
+ mGoals = {}
|
|
|
+ mSelectedGoal = ""
|
|
|
+ mLinuxTextEditor = ""
|
|
|
+ mMacTextEditor = ""
|
|
|
+ mSimulateClean = False
|
|
|
+ mLogUtilityTool = True
|
|
|
+ mShowProgressString = True
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def __init__ (self, goal, logUtilityTool=False):
|
|
|
+ if not argumentIsString (goal):
|
|
|
+ print (BOLD_RED () + "*** Make instanciation: 'goal' argument is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ self.mRuleList = []
|
|
|
+ self.mJobList = []
|
|
|
+ self.mErrorCount = 0
|
|
|
+ self.mModificationDateDictionary = {}
|
|
|
+ self.mGoals = {}
|
|
|
+ self.mSelectedGoal = goal
|
|
|
+ self.mLinuxTextEditor = "gEdit"
|
|
|
+ self.mMacTextEditor = "TextEdit"
|
|
|
+ self.mLogUtilityTool = logUtilityTool
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def doNotShowProgressString (self) :
|
|
|
+ self.mShowProgressString = False
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def addRule (self, rule):
|
|
|
+ if not isinstance (rule, Rule):
|
|
|
+ print (BOLD_RED () + "*** Make.addRule: 'rule' argument is not an instance of Rule type ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ self.mRuleList.append (copy.deepcopy (rule))
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def printRules (self):
|
|
|
+ print (BOLD_BLUE () + "--- Print " + str (len (self.mRuleList)) + " rule" + ("s" if len (self.mRuleList) > 1 else "") + " ---" + ENDC ())
|
|
|
+ for rule in self.mRuleList:
|
|
|
+ message = ""
|
|
|
+ for s in rule.mTargets:
|
|
|
+ message += " \"" + s + "\""
|
|
|
+ print (BOLD_GREEN () + "Target:" + message + ENDC ())
|
|
|
+ for dep in rule.mDependences:
|
|
|
+ print (" Dependence: \"" + dep + "\"")
|
|
|
+ s = " Command: "
|
|
|
+ for cmd in rule.mCommand:
|
|
|
+ s += " \"" + cmd + "\""
|
|
|
+ print (s)
|
|
|
+ print (" Title: \"" + rule.mTitle + "\"")
|
|
|
+ print (" Delete target on error: " + ("yes" if rule.mDeleteTargetOnError else "no"))
|
|
|
+ cleanOp = "none"
|
|
|
+ if rule.mCleanOperation == 1:
|
|
|
+ cleanOp = "delete target file(s)"
|
|
|
+ elif rule.mCleanOperation == 2:
|
|
|
+ dirSet = set ()
|
|
|
+ for s in rule.mTargets:
|
|
|
+ path = os.path.dirname (s)
|
|
|
+ if path != "":
|
|
|
+ dirSet.add (path)
|
|
|
+ cleanOp = "delete target directory:"
|
|
|
+ for s in dirSet:
|
|
|
+ cleanOp += " \"" + s + "\""
|
|
|
+ print (" Clean operation: " + cleanOp)
|
|
|
+ index = 0
|
|
|
+ for postCommand in rule.mPostCommands:
|
|
|
+ index = index + 1
|
|
|
+ s = " Post command " + str (index) + ": "
|
|
|
+ for cmd in postCommand.mCommand:
|
|
|
+ s += " \"" + cmd + "\""
|
|
|
+ print (s)
|
|
|
+ print (" Title: \"" + postCommand.mTitle + "\"")
|
|
|
+
|
|
|
+ print (BOLD_BLUE () + "--- End of print rule ---" + ENDC ())
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def writeRuleDependancesInDotFile (self, dotFileName):
|
|
|
+ s = "digraph G {\n"
|
|
|
+ s += " node [fontname=courier]\n"
|
|
|
+ arrowSet = set ()
|
|
|
+ for rule in self.mRuleList:
|
|
|
+ for target in rule.mTargets:
|
|
|
+ s += ' "' + target + '" [shape=rectangle]\n'
|
|
|
+ for dep in rule.mDependences:
|
|
|
+ arrowSet.add (' "' + target + '" -> "' + dep + '"\n')
|
|
|
+ for arrow in arrowSet:
|
|
|
+ s += arrow
|
|
|
+ s += "}\n"
|
|
|
+ f = open (dotFileName, "w")
|
|
|
+ f.write (s)
|
|
|
+ f.close ()
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def checkRules (self):
|
|
|
+ if self.mErrorCount == 0:
|
|
|
+ ruleList = copy.deepcopy (self.mRuleList)
|
|
|
+ index = 0
|
|
|
+ looping = True
|
|
|
+ #--- loop on rules
|
|
|
+ while looping:
|
|
|
+ looping = False
|
|
|
+ while index < len (ruleList):
|
|
|
+ aRule = ruleList [index]
|
|
|
+ index = index + 1
|
|
|
+ #--- Check dependance files have rule for building, or does exist
|
|
|
+ depIdx = 0
|
|
|
+ while depIdx < len (aRule.mDependences):
|
|
|
+ dep = aRule.mDependences [depIdx]
|
|
|
+ depIdx = depIdx + 1
|
|
|
+ hasBuildRule = False
|
|
|
+ for r in ruleList:
|
|
|
+ for target in r.mTargets:
|
|
|
+ if dep == target:
|
|
|
+ hasBuildRule = True
|
|
|
+ break
|
|
|
+ if not hasBuildRule:
|
|
|
+ looping = True
|
|
|
+ if not os.path.exists (os.path.abspath (dep)):
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ print (BOLD_RED () + "Check rules error: '" + dep + "' does not exist, and there is no rule for building it." + ENDC ())
|
|
|
+ depIdx = depIdx - 1
|
|
|
+ aRule.mDependences.pop (depIdx)
|
|
|
+ #--- Rule with no dependances
|
|
|
+ if len (aRule.mDependences) == 0 :
|
|
|
+ looping = True
|
|
|
+ index = index - 1
|
|
|
+ ruleList.pop (index)
|
|
|
+ idx = 0
|
|
|
+ while idx < len (ruleList):
|
|
|
+ r = ruleList [idx]
|
|
|
+ idx = idx + 1
|
|
|
+ for target in aRule.mTargets:
|
|
|
+ while r.mDependences.count (target) > 0 :
|
|
|
+ r.mDependences.remove (target)
|
|
|
+ #--- Error if rules remain
|
|
|
+ if len (ruleList) > 0:
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ print (BOLD_RED () + "Check rules error; circulary dependances between:" + ENDC ())
|
|
|
+ for aRule in ruleList:
|
|
|
+ targetList = ""
|
|
|
+ for target in aRule.mTargets:
|
|
|
+ targetList += " '" + aRule.mTarget + "'"
|
|
|
+ print (BOLD_RED () + " - " + targetList + ", depends from:" + ENDC ())
|
|
|
+ for dep in aRule.mDependences:
|
|
|
+ print (BOLD_RED () + " '" + dep + "'" + ENDC ())
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def existsJobForTarget (self, target):
|
|
|
+ for job in self.mJobList:
|
|
|
+ for aTarget in job.mTargets:
|
|
|
+ if aTarget == target:
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def makeJob (self, target): # Return a bool indicating wheither the target should be built
|
|
|
+ #--- If there are errors, return immediatly
|
|
|
+ if self.mErrorCount != 0:
|
|
|
+ return False
|
|
|
+ #--- Target already in job list ?
|
|
|
+ if self.existsJobForTarget (target):
|
|
|
+ return True # yes, return target will be built
|
|
|
+ #--- Find a rule for making the target
|
|
|
+ absTarget = os.path.abspath (target)
|
|
|
+ rule = None
|
|
|
+ matchCount = 0
|
|
|
+ for r in self.mRuleList:
|
|
|
+ for aTarget in r.mTargets:
|
|
|
+ if target == aTarget:
|
|
|
+ matchCount = matchCount + 1
|
|
|
+ rule = r
|
|
|
+ if matchCount == 0:
|
|
|
+ absTarget = os.path.abspath (target)
|
|
|
+ if not os.path.exists (absTarget):
|
|
|
+ print (BOLD_RED () + "No rule for making '" + target + "'" + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ return False # Error or target exists, and no rule for building it
|
|
|
+ elif matchCount > 1:
|
|
|
+ print (BOLD_RED () + str (matchCount) + " rules for making '" + target + "'" + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ return False # Error
|
|
|
+ #--- Target file does not exist, and 'rule' variable indicates how build it
|
|
|
+ appendToJobList = not os.path.exists (absTarget)
|
|
|
+ #--- Build primary dependences
|
|
|
+ jobDependenceFiles = []
|
|
|
+ for dependence in rule.mDependences:
|
|
|
+ willBeBuilt = self.makeJob (dependence)
|
|
|
+ if willBeBuilt:
|
|
|
+ jobDependenceFiles.append (dependence)
|
|
|
+ appendToJobList = True
|
|
|
+ #--- Check primary file modification dates
|
|
|
+ if not appendToJobList:
|
|
|
+ targetDateModification = os.path.getmtime (absTarget)
|
|
|
+ for source in rule.mDependences:
|
|
|
+ sourceDateModification = os.path.getmtime (source)
|
|
|
+ if targetDateModification < sourceDateModification:
|
|
|
+ appendToJobList = True
|
|
|
+ break
|
|
|
+ #--- Check for secondary dependancy files
|
|
|
+ if not appendToJobList:
|
|
|
+ targetDateModification = os.path.getmtime (absTarget)
|
|
|
+ if targetDateModification < rule.mSecondaryMostRecentModificationDate:
|
|
|
+ appendToJobList = True
|
|
|
+ #--- Append to job list
|
|
|
+ if appendToJobList:
|
|
|
+ self.mJobList.append (Job (
|
|
|
+ rule.mTargets,
|
|
|
+ jobDependenceFiles,
|
|
|
+ rule.mCommand,
|
|
|
+ rule.mPostCommands,
|
|
|
+ rule.mPriority,
|
|
|
+ rule.mTitle,
|
|
|
+ rule.mOpenSourceOnError,
|
|
|
+ self.mLogUtilityTool
|
|
|
+ ))
|
|
|
+ #--- Return
|
|
|
+ return appendToJobList
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+ #Job state
|
|
|
+ # 0: waiting
|
|
|
+ # 1:running
|
|
|
+ # 2: waiting for executing post command
|
|
|
+ # 3:executing for executing post command
|
|
|
+ # 4: completed
|
|
|
+
|
|
|
+ def runJobs (self, maxConcurrentJobs, showCommand):
|
|
|
+ if self.mErrorCount == 0:
|
|
|
+ if len (self.mJobList) == 0:
|
|
|
+ print (BOLD_BLUE () + "Nothing to make." + ENDC ())
|
|
|
+ else:
|
|
|
+ #--- Sort jobs following their priorities
|
|
|
+ self.mJobList = sorted (self.mJobList, key=operator.attrgetter("mPriority"), reverse=True)
|
|
|
+ #--- Run
|
|
|
+ if maxConcurrentJobs <= 0:
|
|
|
+ maxConcurrentJobs = processorCount () - maxConcurrentJobs
|
|
|
+ jobCount = 0 ;
|
|
|
+ terminationSemaphore = threading.Semaphore (0)
|
|
|
+ displayLock = threading.Lock ()
|
|
|
+ loop = True
|
|
|
+ returnCode = 0
|
|
|
+ totalJobCount = float (len (self.mJobList))
|
|
|
+ launchedJobCount = 0.0
|
|
|
+ while loop:
|
|
|
+ #--- Launch jobs in parallel
|
|
|
+ for job in self.mJobList:
|
|
|
+ if (returnCode == 0) and (jobCount < maxConcurrentJobs):
|
|
|
+ if (job.mState == 0) and (len (job.mRequiredFiles) == 0):
|
|
|
+ #--- Create target directory if does not exist
|
|
|
+ for aTarget in job.mTargets:
|
|
|
+ absTargetDirectory = os.path.dirname (os.path.abspath (aTarget))
|
|
|
+ if not os.path.exists (absTargetDirectory):
|
|
|
+ displayLock.acquire ()
|
|
|
+ runCommand (
|
|
|
+ ["mkdir", "-p", os.path.dirname (aTarget)], "Making \"" + os.path.dirname (aTarget) + "\" directory",
|
|
|
+ showCommand,
|
|
|
+ job.mLogUtilityTool
|
|
|
+ )
|
|
|
+ displayLock.release ()
|
|
|
+ #--- Progress string
|
|
|
+ launchedJobCount += 1.0
|
|
|
+ if self.mShowProgressString:
|
|
|
+ progressString = "[{0:3d}%] ".format (int (100.0 * launchedJobCount / totalJobCount))
|
|
|
+ else:
|
|
|
+ progressString = ""
|
|
|
+ #--- Run job
|
|
|
+ job.run (displayLock, terminationSemaphore, showCommand, progressString)
|
|
|
+ jobCount = jobCount + 1
|
|
|
+ job.mState = 1 # Means is running
|
|
|
+ elif job.mState == 2: # Waiting for executing post command
|
|
|
+ job.mReturnCode = None # Means post command not terminated
|
|
|
+ job.runPostCommand (displayLock, terminationSemaphore, showCommand)
|
|
|
+ jobCount = jobCount + 1
|
|
|
+ job.mState = 3 # Means post command is running
|
|
|
+ #--- Wait for a job termination
|
|
|
+ #print ("wait " + str (jobCount) + " " + str (len (self.mJobList)))
|
|
|
+ terminationSemaphore.acquire ()
|
|
|
+ #--- Checks for terminated jobs
|
|
|
+ index = 0
|
|
|
+ while index < len (self.mJobList):
|
|
|
+ job = self.mJobList [index]
|
|
|
+ index = index + 1
|
|
|
+ if (job.mState == 1) and (job.mReturnCode != None) and (job.mReturnCode == 0) : # Terminated without error
|
|
|
+ jobCount = jobCount - 1
|
|
|
+ for aTarget in job.mTargets:
|
|
|
+ if not os.path.exists (os.path.abspath (aTarget)): # Warning: target does not exist
|
|
|
+ displayLock.acquire ()
|
|
|
+ print (MAGENTA () + BOLD () + "Warning: target \"" + aTarget + "\" was not created by rule execution." + ENDC ())
|
|
|
+ displayLock.release ()
|
|
|
+ if len (job.mPostCommands) > 0:
|
|
|
+ job.mState = 2 # Ready to execute next post command
|
|
|
+ else:
|
|
|
+ job.mState = 4 # Completed
|
|
|
+ index = index - 1 # For removing job from list
|
|
|
+ elif (job.mState == 1) and (job.mReturnCode != None) and (job.mReturnCode > 0) : # terminated with error : exit
|
|
|
+ jobCount = jobCount - 1
|
|
|
+ job.mState = 4 # Means Terminated
|
|
|
+ index = index - 1 # For removing job from list
|
|
|
+ if job.mOpenSourceOnError:
|
|
|
+ for line in job.mOutputLines:
|
|
|
+ components = line.split (':')
|
|
|
+ if (len (components) > 1) and os.path.exists (os.path.abspath (components [0])) :
|
|
|
+ if sys.platform == "darwin":
|
|
|
+ os.system ("open -a \"" + self.mMacTextEditor + "\" \"" + components [0] + "\"")
|
|
|
+ elif sys.platform == "linux2":
|
|
|
+ os.system ("\"" + self.mLinuxTextEditor + "\" \"" + components [0] + "\"")
|
|
|
+ elif (job.mState == 3) and (job.mReturnCode == 0): # post command is terminated without error
|
|
|
+ jobCount = jobCount - 1
|
|
|
+ job.mPostCommands.pop (0) # Remove completed post command
|
|
|
+ if len (job.mPostCommands) > 0:
|
|
|
+ job.mState = 2 # Ready to execute next post command
|
|
|
+ else:
|
|
|
+ job.mState = 4 # Completed
|
|
|
+ index = index - 1 # For removing job from list
|
|
|
+ elif (job.mState == 3) and (job.mReturnCode > 0): # post command is terminated with error
|
|
|
+ jobCount = jobCount - 1
|
|
|
+ job.mState = 4 # Completed
|
|
|
+ index = index - 1 # For removing job from list
|
|
|
+ elif job.mState == 4: # Completed: delete job
|
|
|
+ index = index - 1
|
|
|
+ self.mJobList.pop (index) # Remove terminated job
|
|
|
+ #displayLock.acquire ()
|
|
|
+ #print ("Completed '" + job.mTitle + "'")
|
|
|
+ #--- Remove dependences from this job
|
|
|
+ idx = 0
|
|
|
+ while idx < len (self.mJobList):
|
|
|
+ aJob = self.mJobList [idx]
|
|
|
+ idx = idx + 1
|
|
|
+ for aTarget in job.mTargets:
|
|
|
+ while aJob.mRequiredFiles.count (aTarget) > 0 :
|
|
|
+ aJob.mRequiredFiles.remove (aTarget)
|
|
|
+ #print (" Removed from '" + aJob.mTitle + "': " + str (len (aJob.mRequiredFiles)))
|
|
|
+ #displayLock.release ()
|
|
|
+ #--- Signal error ?
|
|
|
+ if (job.mReturnCode > 0) and (returnCode == 0):
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ print (BOLD_RED () + "Return code: " + str (job.mReturnCode) + ENDC ())
|
|
|
+ if (returnCode == 0) and (jobCount > 0) :
|
|
|
+ print ("Wait for job termination...")
|
|
|
+ returnCode = job.mReturnCode
|
|
|
+ loop = (len (self.mJobList) > 0) if (returnCode == 0) else (jobCount > 0)
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def searchFileInRelativeDirectories (self, file, directoryList): # returns "" if not found, register error
|
|
|
+ matchCount = 0
|
|
|
+ result = ""
|
|
|
+ for sourceDir in directoryList:
|
|
|
+ sourcePath = sourceDir + "/" + file
|
|
|
+ if os.path.exists (os.path.abspath (sourcePath)):
|
|
|
+ matchCount = matchCount + 1
|
|
|
+ prefix = os.path.commonprefix ([os.getcwd (), sourcePath])
|
|
|
+ result = sourcePath [len (prefix):]
|
|
|
+ if result [0] == '/' :
|
|
|
+ result = result [1:]
|
|
|
+ if matchCount == 0:
|
|
|
+ print (BOLD_RED () + "Cannot find '" + file + "'" + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ elif matchCount > 1:
|
|
|
+ print (BOLD_RED () + str (matchCount) + " source files for making '" + file + "'" + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ result = ""
|
|
|
+ return result
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def searchFileInDirectories (self, file, directoryList): # returns "" if not found, register error
|
|
|
+ matchCount = 0
|
|
|
+ result = ""
|
|
|
+ for sourceDir in directoryList:
|
|
|
+ sourcePath = sourceDir + "/" + file
|
|
|
+ if os.path.exists (os.path.abspath (sourcePath)):
|
|
|
+ matchCount = matchCount + 1
|
|
|
+ result = sourcePath
|
|
|
+ if matchCount == 0:
|
|
|
+ print (BOLD_RED () + "Cannot find '" + file + "'" + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ elif matchCount > 1:
|
|
|
+ print (BOLD_RED () + str (matchCount) + " source files for making '" + file + "'" + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+ result = ""
|
|
|
+ return result
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def addGoal (self, goal, targetList, message):
|
|
|
+ if not argumentIsString (goal):
|
|
|
+ print (BOLD_RED () + "*** Make.addGoal: 'goal' first argument is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if not type (targetList) is list:
|
|
|
+ print (BOLD_RED () + "*** Make.addGoal: 'targetList' second argument is not a list ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ else:
|
|
|
+ for aTarget in targetList:
|
|
|
+ if not argumentIsString (aTarget):
|
|
|
+ print (BOLD_RED () + "*** Make.addGoal: an element of 'targetList' second argument 'targets' is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if not argumentIsString (message):
|
|
|
+ print (BOLD_RED () + "*** Make.addGoal: 'message' third argument is not a string ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if (goal in self.mGoals) or (goal == "clean") :
|
|
|
+ self.enterError ("The '" + goal + "' goal is already defined")
|
|
|
+ else:
|
|
|
+ self.mGoals [goal] = (targetList, message)
|
|
|
+ #print ('%s' % ', '.join(map(str, self.mGoals)))
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def printGoals (self):
|
|
|
+ print (BOLD_BLUE () + "--- Print " + str (len (self.mGoals)) + " goal" + ("s" if len (self.mGoals) > 1 else "") + " ---" + ENDC ())
|
|
|
+ for goalKey in self.mGoals.keys ():
|
|
|
+ print (BOLD_GREEN () + "Goal: \"" + goalKey + "\"" + ENDC ())
|
|
|
+ (targetList, message) = self.mGoals [goalKey]
|
|
|
+ for target in targetList:
|
|
|
+ print (" Target: \"" + target + "\"")
|
|
|
+ print (" Message: \"" + message + "\"")
|
|
|
+
|
|
|
+ print (BOLD_BLUE () + "--- End of print goals ---" + ENDC ())
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def runGoal (self, maxConcurrentJobs, showCommand):
|
|
|
+ if not isinstance (maxConcurrentJobs, int):
|
|
|
+ print (BOLD_RED () + "*** Make.runGoal: 'maxConcurrentJobs' first argument is not an integer ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if not isinstance (showCommand, bool):
|
|
|
+ print (BOLD_RED () + "*** Make.runGoal: 'showCommand' second argument is not a boolean ***" + ENDC ())
|
|
|
+ traceback.print_stack ()
|
|
|
+ sys.exit (1)
|
|
|
+ if self.mSelectedGoal in self.mGoals :
|
|
|
+ (targetList, message) = self.mGoals [self.mSelectedGoal]
|
|
|
+ for target in targetList:
|
|
|
+ self.makeJob (target)
|
|
|
+ self.runJobs (maxConcurrentJobs, showCommand)
|
|
|
+ if self.mErrorCount > 0:
|
|
|
+ for rule in self.mRuleList:
|
|
|
+ for aTarget in rule.mTargets:
|
|
|
+ if rule.mDeleteTargetOnError and os.path.exists (os.path.abspath (aTarget)):
|
|
|
+ runCommand (["rm", aTarget], "Delete \"" + aTarget + "\" on error", showCommand, self.mLogUtilityTool)
|
|
|
+ elif self.mSelectedGoal == "clean" :
|
|
|
+ filesToRemoveList = []
|
|
|
+ directoriesToRemoveSet = set ()
|
|
|
+ for rule in self.mRuleList:
|
|
|
+ if rule.mCleanOperation == 1: # Delete target
|
|
|
+ for aTarget in rule.mTargets:
|
|
|
+ filesToRemoveList.append (aTarget)
|
|
|
+ elif rule.mCleanOperation == 2: # Delete target directories
|
|
|
+ for aTarget in rule.mTargets:
|
|
|
+ dirPath = os.path.dirname (aTarget)
|
|
|
+ if dirPath == "":
|
|
|
+ filesToRemoveList.append (aTarget)
|
|
|
+ else:
|
|
|
+ directoriesToRemoveSet.add (dirPath)
|
|
|
+ for dir in directoriesToRemoveSet:
|
|
|
+ if os.path.exists (os.path.abspath (dir)):
|
|
|
+ if self.mSimulateClean:
|
|
|
+ print (MAGENTA () + BOLD () + "Simulated clean command: " + ENDC () + "rm -fr '" + dir + "'")
|
|
|
+ else:
|
|
|
+ runCommand (["rm", "-fr", dir], "Removing \"" + dir + "\"", showCommand, self.mLogUtilityTool)
|
|
|
+ for file in filesToRemoveList:
|
|
|
+ if os.path.exists (os.path.abspath (file)):
|
|
|
+ if self.mSimulateClean:
|
|
|
+ print (MAGENTA () + BOLD () + "Simulated clean command: " + ENDC () + "rm -f '" + file + "'")
|
|
|
+ else:
|
|
|
+ runCommand (["rm", "-f", file], "Deleting \"" + file + "\"", showCommand, self.mLogUtilityTool)
|
|
|
+ else:
|
|
|
+ errorMessage = "The '" + self.mSelectedGoal + "' goal is not defined; defined goals:"
|
|
|
+ for key in self.mGoals:
|
|
|
+ (targetList, message) = self.mGoals [key]
|
|
|
+ errorMessage += "\n '" + key + "': " + message
|
|
|
+ print (BOLD_RED () + errorMessage + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def simulateClean (self):
|
|
|
+ self.mSimulateClean = True
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def enterError (self, message):
|
|
|
+ print (BOLD_RED () + message + ENDC ())
|
|
|
+ self.mErrorCount = self.mErrorCount + 1
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def printErrorCountAndExitOnError (self):
|
|
|
+ if self.mErrorCount == 1:
|
|
|
+ print (BOLD_RED () + "1 error." + ENDC ())
|
|
|
+ sys.exit (1)
|
|
|
+ elif self.mErrorCount > 1:
|
|
|
+ print (BOLD_RED () + str (self.mErrorCount) + " errors." + ENDC ())
|
|
|
+ sys.exit (1)
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def printErrorCount (self):
|
|
|
+ if self.mErrorCount == 1:
|
|
|
+ print (BOLD_RED () + "1 error." + ENDC ())
|
|
|
+ elif self.mErrorCount > 1:
|
|
|
+ print (BOLD_RED () + str (self.mErrorCount) + " errors." + ENDC ())
|
|
|
+
|
|
|
+ #····················································································································*
|
|
|
+
|
|
|
+ def errorCount (self):
|
|
|
+ return self.mErrorCount
|
|
|
+
|
|
|
+#——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*
|