|
# Python script to help embedding Source Index in PDB files. |
|
# See end of file for license. |
|
|
|
import argparse |
|
import glob |
|
import os |
|
import subprocess |
|
|
|
|
|
def find_best_same_path(paths: list[str]): |
|
i = 1 |
|
result = "" |
|
while True: |
|
result = paths[0][:i] |
|
if len(result) < i: |
|
return result |
|
for path in paths: |
|
if path[:i] != result: |
|
return result[:-1] |
|
i = i + 1 |
|
|
|
|
|
class SourceIndexMapper: |
|
def __init__(self, path: str, url: str, /): |
|
# Ensure path and url has no trailing slash |
|
if path[-1] in ("\\", "/"): |
|
self.path = path[:-1] |
|
else: |
|
self.path = path |
|
if url[-1] == "/": |
|
self._url = url[:-1] |
|
else: |
|
self._url = url |
|
|
|
# Check if the specified path is acceptable |
|
def accepts(self, path: str): |
|
return path.startswith(os.path.abspath(self.path) + os.sep) |
|
|
|
# Transform from absolute path to URL |
|
def transform(self, file: str): |
|
# The substring gives path that starts with "/" |
|
return self._url + file.replace("\\", "/")[len(self.path) :] |
|
|
|
@property |
|
def url(self): |
|
return self._url |
|
|
|
|
|
class SrcsrvWriter: |
|
def __init__(self, output: str) -> None: |
|
self._output = output |
|
self.files = [] # type: list[tuple[str, str]] |
|
|
|
def add(self, localfile: str, urlfile: str): |
|
self.files.append((localfile, urlfile)) |
|
return self |
|
|
|
def write(self): |
|
if not self.files: |
|
return False |
|
file = open(self._output, "w", encoding="UTF-8", newline="") |
|
base_http = find_best_same_path([x[1] for x in self.files]) |
|
len_base_http = len(base_http) |
|
file.write("SRCSRV: ini ------------------------------------------------\r\n") |
|
file.write("VERSION=2\r\nVERCTRL=http\r\n") |
|
file.write("SRCSRV: variables ------------------------------------------\r\n") |
|
file.write(f"HTTP_ALIAS={base_http}\r\n") |
|
file.write("HTTP_EXTRACT_TARGET=%HTTP_ALIAS%%var2%\r\nSRCSRVTRG=%HTTP_EXTRACT_TARGET%\r\n") |
|
file.write("SRCSRV: source files ---------------------------------------\r\n") |
|
for f in self.files: |
|
file.write(f[0]) |
|
file.write("*") |
|
file.write(f[1][len_base_http:]) |
|
file.write("\r\n") |
|
file.write("SRCSRV: end ------------------------------------------------\r\n") |
|
file.close() |
|
return True |
|
|
|
@property |
|
def output(self): |
|
return self._output |
|
|
|
|
|
source_mappers = [] # type: list[SourceIndexMapper] |
|
|
|
|
|
def get_source_list(srctool: str, pdb: str): |
|
result = subprocess.run([srctool, "-r", pdb], capture_output=True, encoding="UTF-8") |
|
if result.returncode == 4294967295: |
|
raise RuntimeError("srctool.exe returns -1") |
|
return result.stdout.split("\n", result.returncode)[:-1] |
|
|
|
|
|
def embed_pdb(pdbstr: str, pdb: str, srcsrv: str): |
|
subprocess.run([pdbstr, "-w", f"-p:{pdb}", "-s:srcsrv", f"-i:{srcsrv}"], check=True) |
|
|
|
|
|
def main(): |
|
global source_mappers |
|
parser = argparse.ArgumentParser() |
|
parser.add_argument("--srctool", help="path to srctool.exe.", default="srctool.exe") |
|
parser.add_argument("--pdbstr", help="path to pdbstr.exe.", default="pdbstr.exe") |
|
parser.add_argument("--dry", help="don't embed, only write the _srcsrv.txt file.", action="store_true") |
|
parser.add_argument( |
|
"--source", |
|
help="path-URL tuple to match and substitute in this order.", |
|
metavar=("PATH", "URL"), |
|
action="append", |
|
nargs=2, |
|
) |
|
parser.add_argument("pdb", help="PDB files to map and source index.", nargs="+") |
|
args = parser.parse_args() |
|
if not args.source or len(args.source) == 0: |
|
parser.error("at least one --source is required") |
|
for mapping in args.source: |
|
source_mappers.append(SourceIndexMapper(*mapping)) |
|
for pdb_list in args.pdb: |
|
for pdb in glob.glob(pdb_list): |
|
files = get_source_list(args.srctool, pdb) |
|
srcsrv = SrcsrvWriter(os.path.basename(pdb) + "_srcsrv.txt") |
|
for file in files: |
|
# Find transform |
|
for mapper in source_mappers: |
|
if mapper.accepts(file): |
|
srcsrv.add(file, mapper.transform(file)) |
|
break |
|
if srcsrv.write() and (not args.dry): |
|
embed_pdb(args.pdbstr, pdb, srcsrv.output) |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |
|
|
|
# ------------------------------------------------------------------------------ |
|
# This software is available under 2 licenses -- choose whichever you prefer. |
|
# ------------------------------------------------------------------------------ |
|
# ALTERNATIVE A - MIT License |
|
# |
|
# Copyright (c) 2023 Miku AuahDark |
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
# this software and associated documentation files (the "Software"), to deal in |
|
# the Software without restriction, including without limitation the rights to |
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
|
# of the Software, and to permit persons to whom the Software is furnished to do |
|
# so, subject to the following conditions: |
|
# |
|
# The above copyright notice and this permission notice shall be included in all |
|
# copies or substantial portions of the Software. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
# SOFTWARE. |
|
# ------------------------------------------------------------------------------ |
|
# ALTERNATIVE B - Public Domain (www.unlicense.org) |
|
# |
|
# This is free and unencumbered software released into the public domain. |
|
# Anyone is free to copy, modify, publish, use, compile, sell, or distribute this |
|
# software, either in source code form or as a compiled binary, for any purpose, |
|
# commercial or non-commercial, and by any means. |
|
# |
|
# In jurisdictions that recognize copyright laws, the author or authors of this |
|
# software dedicate any and all copyright interest in the software to the public |
|
# domain. We make this dedication for the benefit of the public at large and to |
|
# the detriment of our heirs and successors. We intend this dedication to be an |
|
# overt act of relinquishment in perpetuity of all present and future rights to |
|
# this software under copyright law. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
# AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
|
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
# ------------------------------------------------------------------------------ |