OpenMiInk/third_party/kde-ci-utilities/components/CommonUtils.py
Hellings b8c8ff98e2 Hi
2026-10-16 09:05:56 -04:00

130 lines
5.3 KiB
Python

import os
import sys
import shutil
import hashlib
import collections
from typing import Dict, List
import packaging.tags
# Returns the absolute path to the base directory of the CI Tooling checkout we are running from
def scriptsBaseDirectory() -> str:
# Where is this file located?
fileLocation = os.path.dirname( os.path.realpath(__file__) )
# We are at /components, therefore the relative location of the base directory is ../
relativeLocation = '../'
# Finally we can deduce the absolute path to the base directory
return os.path.realpath( os.path.join(fileLocation, relativeLocation) )
# Determines where the build directory should be located
# This is done based on the provided source directory and the definition of whether this should be an in or out of source build
def buildDirectoryForSources( sources: str, inSourceBuild: bool ) -> str:
# If we are an in source build, then the build and source directories are the same
if inSourceBuild:
return sources
# Otherwise, it is a folder named 'build' within the source tree
return os.path.join( sources, 'build' )
# Determines which of the given files exists first in the specified directory
def firstPresentFile( searchRoot: str, filesToFind: List[str] ) -> str:
# Let's start searching....
availableFiles = [ filename for filename in filesToFind if os.path.isfile(os.path.join(searchRoot, filename)) ]
# Return the first hit we found, returning an empty string if none were found
return next( iter(availableFiles), '' )
# Determines where the first of a given set of files is in a given set of directories
def firstPresentFileInPaths( searchRoots: List[str], filesToFind: List[str] ) -> str:
# Start searching...
for rootPath in searchRoots:
# Look in this directory
foundFile = firstPresentFile( rootPath, filesToFind )
# If we found something, we're finished
if foundFile != '':
# Make a usable path and return it
return os.path.join( rootPath, foundFile )
# If we failed to find anything, return blank
return ''
def recursiveDirectoryCopy(sourceDirectory: str, destinationDirectory: str) -> None:
# Copy a directory structure overwriting existing files
for root, _dirs, files in os.walk(sourceDirectory):
# Ensure we have a relative path for root as we'll need it quite a bit shortly
currentDirectory = os.path.relpath( root, sourceDirectory )
# Make sure the directory exists in our destination
currentDestination = os.path.join( destinationDirectory, currentDirectory )
if not os.path.isdir(currentDestination):
os.makedirs(currentDestination)
# Now we can copy the various files within in turn
for fileToCopy in files:
# Determine the full path to the source file
fileSource = os.path.join(root, fileToCopy)
# Determine where to copy the file to
fileDestination = os.path.join(currentDestination, fileToCopy)
# Copy it!
shutil.copyfile( fileSource, fileDestination )
# Converts a path to a relative one, to allow for it to be passed to os.path.join
# This is primarily relevant on Windows, where full paths have the drive letter, and thus can be simply joined together as you can on Unix systems
def makePathRelative(path: str) -> str:
# If we're on Windows, chop the drive letter off...
if sys.platform == "win32":
return path[3:]
# Otherwise we just drop the starting slash off
return path[1:]
# Convenience function to generate the SHA-256 hash of a given file
# We read files in small chunks, to ensure we can handle large files if needed
def generateFileChecksum( filenameToHash: str ) -> str:
# Grab our hasher
hasher = hashlib.sha256()
# Open the file
with open(filenameToHash, 'rb') as fileToHash:
# Read chunks until there are no more, passing them to the hasher as we go
fileChunk = fileToHash.read(65336)
while len(fileChunk) > 0:
hasher.update(fileChunk)
fileChunk = fileToHash.read(65336)
# All done, return the generated hash
return hasher.hexdigest()
RecursiveDict = Dict[str, 'RecursiveDict']
# Convenience function to recursively merge Python dictionaries
# This is of particular importance to the configuration loading code
def recursiveUpdate(d: RecursiveDict, u: RecursiveDict) -> RecursiveDict:
# Start by going over all the items in the dictionary that is being merged in
for key, value in u.items():
# If our value contains a mapping (dict, list, etc) then recurse into it
# Otherwise just update the value
if isinstance(value, collections.abc.Mapping):
d[key] = recursiveUpdate( d.get(key, {}), value )
else:
d[key] = value
# Return the merged values
return d
# Checks whether we are running on a platform with the "manylinux" tag.
# It can be useful for determining whether the system library is glibc or something else.
def ismanylinux() -> bool:
for tag in packaging.tags.sys_tags():
if tag.platform.startswith('linux_'):
continue
if tag.platform.startswith('manylinux_'):
return True
if tag.platform.startswith('musllinux_'):
return False
print(f"Unknown platform tag: {tag.platform}")
return False
return False