mirror of
https://github.com/darlinghq/darling.git
synced 2025-02-17 08:09:10 +00:00
Rewrite stub generator to also generate CMakeLists
This combines the C function stub generator and the Objective-C class generator. It also detects the dylib current and compat versions. It basically creates a stub folder that you can copy directly into /src.
This commit is contained in:
parent
30e8242ada
commit
fa856191b7
224
tools/darling-stub-gen
Executable file
224
tools/darling-stub-gen
Executable file
@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys, os, subprocess, re
|
||||
|
||||
# Data
|
||||
library = False
|
||||
framework = False
|
||||
private_framework = False
|
||||
uses_objc = False
|
||||
full_path = ""
|
||||
output_dir = ""
|
||||
target_name = ""
|
||||
header_dir = ""
|
||||
source_dir = ""
|
||||
|
||||
# Constants
|
||||
library_prefix = "/usr/lib/"
|
||||
framework_prefix = "/System/Library/Frameworks/"
|
||||
private_framework_prefix = "/System/Library/PrivateFrameworks/"
|
||||
|
||||
class_dump = "~/bin/class-dump"
|
||||
|
||||
copyright ="""/*
|
||||
This file is part of Darling.
|
||||
|
||||
Copyright (C) 2017 Lubos Dolezel
|
||||
|
||||
Darling is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Darling is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
"""
|
||||
|
||||
c_func_impl_stub = """
|
||||
void* %s(void) {
|
||||
if (verbose) puts("STUB: %s called");
|
||||
return NULL;
|
||||
}
|
||||
"""
|
||||
|
||||
msg_handling = """- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
|
||||
return [NSMethodSignature signatureWithObjCTypes: \"v@:\"];
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)anInvocation {
|
||||
NSLog(@\"Stub called: %@ in %@\", NSStringFromSelector([anInvocation selector]), [self class]);
|
||||
}
|
||||
|
||||
"""
|
||||
# Utility functions
|
||||
def usage():
|
||||
print("Usage: " + sys.argv[0] + " <Mach-O> <output directory>")
|
||||
exit(1)
|
||||
|
||||
def extract_library_name(name):
|
||||
prefix_len = len(library_prefix) + len("lib")
|
||||
ext_len = len(".dylib")
|
||||
|
||||
return name[prefix_len : len(name) - ext_len]
|
||||
|
||||
def extract_framework_name(name):
|
||||
return name[name.rfind("/") + 1 :]
|
||||
|
||||
# Main program start
|
||||
if len(sys.argv) != 3:
|
||||
usage()
|
||||
|
||||
full_path = sys.argv[1]
|
||||
output_dir = sys.argv[2]
|
||||
|
||||
try:
|
||||
os.makedirs(output_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
|
||||
if len(full_path) > len(library_prefix) and full_path[:len(library_prefix)] == library_prefix:
|
||||
library = True
|
||||
target_name = extract_library_name(full_path)
|
||||
elif len(full_path) > len(framework_prefix) and full_path[:len(framework_prefix)] == framework_prefix:
|
||||
framework = True
|
||||
target_name = extract_framework_name(full_path)
|
||||
elif len(full_path) > len(private_framework_prefix) and full_path[:len(private_framework_prefix)] == private_framework_prefix:
|
||||
private_framework = True
|
||||
target_name = extract_framework_name(full_path)
|
||||
else:
|
||||
print("Failed to recognize Mach-O location")
|
||||
exit(1)
|
||||
|
||||
header_dir = output_dir + "/include/" + target_name + "/"
|
||||
source_dir = output_dir + "/src/"
|
||||
|
||||
try:
|
||||
os.makedirs(header_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
try:
|
||||
os.makedirs(source_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
# Get C functions
|
||||
|
||||
c_func_out = subprocess.check_output(["nm", "-Ug", full_path])
|
||||
c_func_out = c_func_out.decode('utf8').strip()
|
||||
|
||||
functions = []
|
||||
for line in c_func_out.splitlines():
|
||||
|
||||
if line == "":
|
||||
continue
|
||||
|
||||
address, id, name = line.split(" ")
|
||||
# Remove the underscore
|
||||
name = name[1 : ]
|
||||
|
||||
if id == "T":
|
||||
functions.append(name)
|
||||
|
||||
c_header = open(header_dir + target_name + ".h", "w")
|
||||
c_source = open(source_dir + target_name + ".c", "w")
|
||||
|
||||
c_header.write(copyright)
|
||||
c_source.write(copyright)
|
||||
|
||||
c_source.write("""
|
||||
#include <stdlib.h>
|
||||
|
||||
static int verbose = 0;
|
||||
|
||||
__attribute__((constructor))
|
||||
static void initme(void) {
|
||||
verbose = getenv("STUB_VERBOSE") != NULL;
|
||||
}
|
||||
""")
|
||||
|
||||
c_hdr_buffer = "\n#ifndef _%s_H_\n#define _%s_H_\n\n" % (target_name, target_name)
|
||||
|
||||
for funcname in functions:
|
||||
#c_header.write("void* %s(void);\n" % funcname)
|
||||
c_hdr_buffer += "void* %s(void);\n" % funcname
|
||||
c_source.write(c_func_impl_stub % (funcname, funcname))
|
||||
|
||||
cmake = open(output_dir + "/CMakeLists.txt", "w")
|
||||
|
||||
cmake.write("project(%s)\n\n" % target_name)
|
||||
|
||||
# Get current and compat versions
|
||||
|
||||
otool_out = subprocess.check_output(["otool", "-L", full_path])
|
||||
otool_out = otool_out.decode('utf8').strip()
|
||||
version_line = otool_out.splitlines()[1]
|
||||
|
||||
get_versions = re.compile("\\(compatibility version (.*?), current version (.*?)\\)")
|
||||
|
||||
compat, current = get_versions.search(version_line).groups()
|
||||
|
||||
if library:
|
||||
cmake.write("set(DYLIB_INSTALL_NAME \"%s\")\n" % full_path)
|
||||
cmake.write("set(DYLIB_COMPAT_VERSION \"%s\")\n" % compat)
|
||||
cmake.write("set(DYLIB_CURRENT_VERSION \"%s\")\n\n" % current)
|
||||
|
||||
class_dump_output = subprocess.check_output(["class-dump", full_path]).decode('utf8').strip()
|
||||
|
||||
uses_objc = "This file does not contain any Objective-C runtime information." not in class_dump_output
|
||||
|
||||
if uses_objc:
|
||||
class_dump_all = subprocess.check_output(["class-dump", "-H", "-o", header_dir, full_path]).decode('utf8').strip()
|
||||
get_class_names = re.compile("@interface (.+) :.+")
|
||||
classes = get_class_names.findall(class_dump_output)
|
||||
for classname in classes:
|
||||
impl = open(source_dir + classname + ".m", "w")
|
||||
impl.write(copyright)
|
||||
|
||||
impl.write("#import <%s/%s.h>\n\n" % (target_name, target_name))
|
||||
impl.write("@implementation " + classname + "\n\n")
|
||||
impl.write(msg_handling)
|
||||
impl.write("@end\n")
|
||||
|
||||
c_header.write("#import <%s/%s.h>\n" % (target_name, classname))
|
||||
|
||||
if uses_objc:
|
||||
cmake.write("file(GLOB %s_OBJC_SOURCES src/*.m)\n\n" % target_name)
|
||||
|
||||
if library:
|
||||
source_files = "src/%s.c\n${%s_OBJC_SOURCES}" % (target_name, target_name)
|
||||
cmake.write("add_darling_library(%s SHARED %s)\n" % (target_name, source_files))
|
||||
cmake.write("make_fat(%s)\n" % target_name)
|
||||
libraries = "system objc" if uses_objc else "system"
|
||||
cmake.write("target_link_libraries(%s %s)\n" % (target_name, libraries))
|
||||
cmake.write("install(TARGETS %s DESTINATION libexec/darling/usr/lib)\n" % target_name)
|
||||
else:
|
||||
cmake.write("add_framework(%s\n" %target_name)
|
||||
cmake.write(" FAT\n CURRENT_VERSION\n")
|
||||
if private_framework:
|
||||
cmake.write(" PRIVATE\n")
|
||||
cmake.write(" VERSION \"A\"\n\n")
|
||||
cmake.write(" SOURCES\n")
|
||||
cmake.write(" src/%s.c\n" % target_name)
|
||||
if uses_objc:
|
||||
cmake.write(" ${%s_OBJC_SOURCES}\n" % target_name)
|
||||
|
||||
cmake.write("\n")
|
||||
|
||||
cmake.write(" DEPENDENCIES\n")
|
||||
cmake.write(" system\n")
|
||||
if uses_objc:
|
||||
cmake.write(" objc\n")
|
||||
cmake.write(" Foundation\n")
|
||||
cmake.write(")\n")
|
||||
|
||||
c_header.write(c_hdr_buffer)
|
||||
c_header.write("\n#endif\n")
|
Loading…
x
Reference in New Issue
Block a user