File indexing completed on 2024-04-14 05:35:09

0001 #!/usr/bin/python3
0002 
0003 # Outputs dependencies information from a build directory
0004 #
0005 # Copyright (c) 2014 Aleix Pol Gonzalez <aleixpol@kde.org>
0006 #
0007 # Redistribution and use in source and binary forms, with or without
0008 # modification, are permitted provided that the following conditions
0009 # are met:
0010 #
0011 # 1. Redistributions of source code must retain the above copyright
0012 #    notice, this list of conditions and the following disclaimer.
0013 # 2. Redistributions in binary form must reproduce the above copyright
0014 #    notice, this list of conditions and the following disclaimer in the
0015 #    documentation and/or other materials provided with the distribution.
0016 #
0017 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0018 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0019 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0020 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0021 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0022 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0023 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0024 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0026 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027 
0028 # cmake . --trace |& grep ^/ | grep -v CMakeLists.txt | cut -d '(' -f 1 | sort -u
0029 
0030 import subprocess
0031 import os
0032 import re
0033 import json
0034 import argparse
0035 import sys
0036 
0037 def readCache(varName):
0038     f = open("CMakeCache.txt", "r")
0039     for line in f:
0040         m = re.match('(.*?)=(.*)', line)
0041         if m is not None and m.group(1)==varName:
0042             return m.group(2)
0043 
0044 def checkPackageVersion(frameworkName):
0045     value = readCache("FIND_PACKAGE_MESSAGE_DETAILS_%s:INTERNAL" % frameworkName)
0046     if value is None:
0047         return None
0048     m = re.match('.*\\]\\[v(.*?)\\((.*?)\\)\\]', value)
0049     if m:
0050         return { 'used': m.group(1), 'requested': m.group(2) }
0051     else:
0052         return None
0053 
0054 if __name__ == "__main__":
0055     parser = argparse.ArgumentParser(description='Figures out the dependencies of the build directory in the cwd.')
0056 
0057     try:
0058         projectDir = readCache("CMAKE_HOME_DIRECTORY:INTERNAL")
0059     except:
0060         print("Run in a build directory.")
0061         parser.print_help()
0062         sys.exit(1)
0063 
0064     proc = subprocess.Popen(['cmake', '.', '--trace-expand'], stdout=open(os.devnull, "w"), stderr=subprocess.PIPE)
0065     processedFiles = {}
0066     lookedUpPackages = {}
0067 
0068     lastFile = ""
0069     callingFile = ""
0070     for line in proc.stderr:
0071         theLine = line.decode("utf-8")
0072 
0073         m = re.match('.*?:\s*find_package\((.*?) (.*?)\).*', theLine)
0074         if m is not None:
0075             if "$" not in m.group(2):
0076                 lookedUpPackages[m.group(1)] = m.group(2)
0077 
0078         # match file names
0079         # e.g./usr/share/cmake-3.0/Modules/FindPackageMessage.cmake(46):  set(...
0080         m = re.match("(^/.*?)\\(.*", theLine)
0081         if m is not None:
0082             currentFile = m.group(1)
0083             if lastFile != currentFile:
0084                 callingFile = lastFile
0085             lastFile = currentFile
0086             filePath, fileName = os.path.split(currentFile)
0087 
0088             if fileName == "CMakeLists.txt":
0089                 continue
0090 
0091             m = re.match("(.*)Config(Version)?.cmake", fileName)
0092             m2 = re.match("Find(.*).cmake", fileName)
0093             if m2:
0094                 moduleName = m2.group(1)
0095             elif m:
0096                 moduleName = m.group(1)
0097             else:
0098                 continue
0099 
0100             if not moduleName in processedFiles:
0101                 processedFiles[moduleName] = { 'files': set(), 'explicit': False }
0102 
0103             if not 'version' in processedFiles[moduleName]:
0104                 processedFiles[moduleName]['version'] = checkPackageVersion(moduleName)
0105 
0106             processedFiles[moduleName]['files'].add(currentFile)
0107             processedFiles[moduleName]['explicit'] |= (callingFile.endswith("CMakeLists.txt") or callingFile.endswith("Qt5/Qt5Config.cmake") or callingFile.endswith("FindKF5.cmake"))
0108 
0109     print("[")
0110     first = True
0111     for v, value in processedFiles.items():
0112         if not first:
0113             print(',\n', end='')
0114         first = False
0115 
0116         value['files'] = list(value['files'])
0117         value['project'] = v
0118         if v in lookedUpPackages:
0119             if value['version'] is None:
0120                 line = lookedUpPackages[v]
0121                 isVersion = line[:line.find(' ')]
0122 
0123                 if len(isVersion)>0 and isVersion[0].isdigit():
0124                     value['version'] = { 'used': None, 'requested': isVersion }
0125 
0126             del lookedUpPackages[v]
0127 
0128         print("\t%s" % (json.dumps(value)), end='')
0129 
0130     # display missing packages
0131     for v in lookedUpPackages:
0132         if not first:
0133             print(',\n', end='')
0134 
0135         print("\t{ \"project\": \"%s\", \"missing\": true, \"files\": [], \"arguments\": \"%s\", \"explicit\": true }" % (v, lookedUpPackages[v]))
0136 
0137 
0138     print("\n]\n")
0139     proc.wait()