mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-24 01:11:01 +00:00
Simple diagrams in documentation
This commit is contained in:
parent
726777bf6e
commit
72c975c543
6 changed files with 236 additions and 93 deletions
12
ChangeLog
12
ChangeLog
|
@ -1,3 +1,15 @@
|
|||
2024-08-07 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Tools/AGSHtml.m:
|
||||
* Tools/GNUmakefile:
|
||||
* config.mak.in:
|
||||
* configure.ac:
|
||||
* configure:
|
||||
Preliminary changes add inheritance diagrams to class documentation.
|
||||
Uses the graphviz 'dot' tool if it is available at configure time.
|
||||
Just draws class/superclass relationship for now.
|
||||
Should add protocols and prettify boxes later.
|
||||
|
||||
2024-07-25 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Headers/Foundation/NSHashTable.h:
|
||||
|
|
168
Tools/AGSHtml.m
168
Tools/AGSHtml.m
|
@ -40,6 +40,15 @@
|
|||
#define GS_API_MACOSX 100000
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HAVE_DOT)
|
||||
#define expandstringify(X) stringify(X)
|
||||
#define stringify(X) #X
|
||||
static NSString *graphviz = @ expandstringify(HAVE_DOT);
|
||||
#else
|
||||
static NSString *graphviz = nil;
|
||||
#endif
|
||||
|
||||
static int XML_ELEMENT_NODE;
|
||||
static int XML_ENTITY_REF_NODE;
|
||||
static int XML_TEXT_NODE;
|
||||
|
@ -57,6 +66,76 @@ static GSXMLNode *firstElement(GSXMLNode *nodes)
|
|||
return [nodes nextElement];
|
||||
}
|
||||
|
||||
static BOOL
|
||||
graph(NSString *path, NSString *input, BOOL verbose)
|
||||
{
|
||||
BOOL didLaunch = NO;
|
||||
BOOL didWrite = NO;
|
||||
BOOL didSucceed = NO;
|
||||
ENTER_POOL
|
||||
NSTask *task = AUTORELEASE([[NSTask alloc] init]);
|
||||
NSPipe *writePipe = [NSPipe pipe];
|
||||
NSFileHandle *writeHandle = [writePipe fileHandleForWriting];
|
||||
|
||||
[task setLaunchPath: graphviz];
|
||||
[task setArguments: [NSArray arrayWithObjects:
|
||||
@"-Tsvg", @"-o", path, nil]];
|
||||
|
||||
[task setStandardInput: [writePipe fileHandleForReading]];
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
NSLog(@"Graph source '%@'", input);
|
||||
}
|
||||
else
|
||||
{
|
||||
[task setStandardError: [NSFileHandle fileHandleWithNullDevice]];
|
||||
[task setStandardOutput: [NSFileHandle fileHandleWithNullDevice]];
|
||||
}
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
[task launch];
|
||||
didLaunch = YES;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Failed task '%@': %@", path, localException);
|
||||
task = nil; // No need to terminate
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
if (YES == didLaunch)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
NSData *data;
|
||||
|
||||
data = [input dataUsingEncoding: NSUTF8StringEncoding];
|
||||
[writeHandle writeData: data];
|
||||
didWrite = YES;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Failed to write to '%@': %@", path, localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
[writeHandle closeFile];
|
||||
[task waitUntilExit];
|
||||
if ([task terminationStatus] == 0)
|
||||
{
|
||||
didSucceed = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Graphing termination status %d", [task terminationStatus]);
|
||||
}
|
||||
LEAVE_POOL
|
||||
return (didLaunch && didWrite && didSucceed) ? YES : NO;
|
||||
}
|
||||
|
||||
|
||||
@implementation AGSHtml
|
||||
|
||||
static NSMutableSet *textNodes = nil;
|
||||
|
@ -177,18 +256,32 @@ static NSMutableSet *textNodes = nil;
|
|||
{
|
||||
NSString *s;
|
||||
NSString *kind = (f == YES) ? @"rel=\"gsdoc\" href" : @"name";
|
||||
|
||||
s = [self makeURL: r ofType: t isRef: f];
|
||||
if (s)
|
||||
{
|
||||
s = [NSString stringWithFormat: @"<a %@=\"%@\">", kind, s];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSString*) makeURL: (NSString*)r
|
||||
ofType: (NSString*)t
|
||||
isRef: (BOOL)f
|
||||
{
|
||||
NSString *s;
|
||||
NSString *hash = (f == YES) ? @"#" : @"";
|
||||
|
||||
if (f == NO || [localRefs globalRef: r type: t] != nil)
|
||||
if (NO == f || [localRefs globalRef: r type: t] != nil)
|
||||
{
|
||||
s = [NSString stringWithFormat: @"<a %@=\"%@%@$%@\">",
|
||||
kind, hash, t, r];
|
||||
s = [NSString stringWithFormat: @"%@%@$%@",
|
||||
hash, t, r];
|
||||
}
|
||||
else if ((s = [globalRefs globalRef: r type: t]) != nil)
|
||||
{
|
||||
s = [s stringByAppendingPathExtension: @"html"];
|
||||
s = [NSString stringWithFormat: @"<a %@=\"%@%@%@$%@\">",
|
||||
kind, s, hash, t, r];
|
||||
s = [NSString stringWithFormat: @"%@%@%@$%@",
|
||||
s, hash, t, r];
|
||||
}
|
||||
return [s stringByReplacingString: @":" withString: @"$"];
|
||||
}
|
||||
|
@ -353,6 +446,11 @@ static NSMutableSet *textNodes = nil;
|
|||
dict = [refs objectForKey: type];
|
||||
}
|
||||
|
||||
/* Put the index in a div with a class identifying its scope and type
|
||||
* so that CSS can be used to style it.
|
||||
*/
|
||||
[buf appendFormat: @"<div class=\"%@_%@_index\">\n", scope, type];
|
||||
|
||||
if ([type isEqual: @"title"] == YES)
|
||||
{
|
||||
if ([dict count] > 1)
|
||||
|
@ -588,6 +686,7 @@ static NSMutableSet *textNodes = nil;
|
|||
}
|
||||
[buf appendString: @"\n"];
|
||||
}
|
||||
[buf appendString: @"</div>\n"];
|
||||
}
|
||||
|
||||
- (void) outputNode: (GSXMLNode*)node to: (NSMutableString*)buf
|
||||
|
@ -686,19 +785,60 @@ static NSMutableSet *textNodes = nil;
|
|||
classname = [prop objectForKey: @"name"];
|
||||
unit = classname;
|
||||
[buf appendString: indent];
|
||||
[buf appendString: @"<h2>"];
|
||||
[buf appendString: @"<h2 class=\"class\">"];
|
||||
[buf appendString:
|
||||
[self makeAnchor: classname ofType: @"class" name: classname]];
|
||||
if (sup != nil)
|
||||
if ([(sup = [sup stringByTrimmingSpaces]) length] == 0)
|
||||
{
|
||||
sup = [self typeRef: sup];
|
||||
if (sup != nil)
|
||||
sup = nil;
|
||||
}
|
||||
if (sup)
|
||||
{
|
||||
NSString *supref = [self typeRef: sup];
|
||||
|
||||
if (supref != nil)
|
||||
{
|
||||
[buf appendString: @" : "];
|
||||
[buf appendString: sup];
|
||||
[buf appendString: supref];
|
||||
}
|
||||
}
|
||||
[buf appendString: @"</h2>\n"];
|
||||
|
||||
if (graphviz && sup)
|
||||
{
|
||||
NSString *url;
|
||||
|
||||
url = [self makeURL: sup ofType: @"class" isRef: YES];
|
||||
|
||||
if (url)
|
||||
{
|
||||
NSMutableString *dot = [NSMutableString string];
|
||||
NSString *full;
|
||||
NSString *svg;
|
||||
|
||||
[dot appendString: @"digraph inheritance {\n"];
|
||||
[dot appendString: @" rankdir = \"TB\";\n"];
|
||||
[dot appendString: @" node [margin=0 fontcolor=blue"
|
||||
@" fontsize=24 width=0.5 shape=rectangle]\n"];
|
||||
[dot appendFormat: @" {node [URL=\"%@\"] %@}\n", url, sup];
|
||||
[dot appendString: @" ->\n"];
|
||||
[dot appendFormat: @" {node [fontcolor=\"green\"] %@}\n",
|
||||
classname];
|
||||
[dot appendString: @"}"];
|
||||
|
||||
svg = [NSString stringWithFormat:
|
||||
@"class_%@.svg", classname];
|
||||
full = [[fileName stringByDeletingLastPathComponent]
|
||||
stringByAppendingPathComponent: svg];
|
||||
|
||||
if (graph(full, dot, verbose))
|
||||
{
|
||||
[buf appendFormat: @"<img alt=\"%@\" src=\"%@\" />\n",
|
||||
classname, svg];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self outputUnit: node to: buf];
|
||||
unit = nil;
|
||||
classname = nil;
|
||||
|
@ -2609,10 +2749,10 @@ static NSMutableSet *textNodes = nil;
|
|||
*/
|
||||
- (NSString*) typeRef: (NSString*)t
|
||||
{
|
||||
NSString *str = [t stringByTrimmingSpaces];
|
||||
NSString *s;
|
||||
unsigned end = [str length];
|
||||
unsigned start;
|
||||
NSString *str = [t stringByTrimmingSpaces];
|
||||
NSString *s;
|
||||
unsigned end = [str length];
|
||||
unsigned start;
|
||||
NSMutableString *ms = nil;
|
||||
NSRange er;
|
||||
NSRange sr;
|
||||
|
|
|
@ -113,6 +113,9 @@ HTMLLinker_OBJC_FILES = HTMLLinker.m
|
|||
ifeq ($(OBJC_RUNTIME_LIB), ng)
|
||||
AGSHtml.m_FILE_FLAGS+= -fobjc-arc
|
||||
endif
|
||||
ifneq ($(HAVE_DOT), )
|
||||
AGSHtml.m_FILE_FLAGS+= -DHAVE_DOT=$(HAVE_DOT)
|
||||
endif
|
||||
|
||||
# Reset this variable (defined in config.mak) to avoid useless linkage
|
||||
# against the libraries gnustep-base uses.
|
||||
|
|
|
@ -13,6 +13,7 @@ DYNAMIC_LINKER=@DYNAMIC_LINKER@
|
|||
HAVE_LIBXML=@HAVE_LIBXML@
|
||||
HAVE_GNUTLS=@HAVE_GNUTLS@
|
||||
HAVE_BLOCKS=@HAVE_BLOCKS@
|
||||
HAVE_DOT=@HAVE_DOT@
|
||||
|
||||
WITH_FFI=@WITH_FFI@
|
||||
|
||||
|
|
134
configure
vendored
134
configure
vendored
|
@ -658,6 +658,8 @@ GNUSTEP_GDOMAP_PORT_OVERRIDE
|
|||
WARN_FLAGS
|
||||
LDIR_FLAGS
|
||||
INCLUDE_FLAGS
|
||||
HAVE_DOT
|
||||
DOT
|
||||
USE_GMP
|
||||
GS_HAVE_NSURLSESSION
|
||||
HAVE_LIBCURL
|
||||
|
@ -884,7 +886,8 @@ XML_LIBS
|
|||
XSLT_CFLAGS
|
||||
XSLT_LIBS
|
||||
ICU_CFLAGS
|
||||
ICU_LIBS'
|
||||
ICU_LIBS
|
||||
DOT'
|
||||
|
||||
|
||||
# Initialize some variables set by options.
|
||||
|
@ -1653,6 +1656,7 @@ Some influential environment variables:
|
|||
XSLT_LIBS linker flags for XSLT, overriding pkg-config
|
||||
ICU_CFLAGS C compiler flags for ICU, overriding pkg-config
|
||||
ICU_LIBS linker flags for ICU, overriding pkg-config
|
||||
DOT The name or full path of the graphviz dot command.
|
||||
|
||||
Use these variables to override the choices made by `configure' or to help
|
||||
it to find libraries and programs with nonstandard names/locations.
|
||||
|
@ -9355,82 +9359,6 @@ esac
|
|||
#--------------------------------------------------------------------
|
||||
# Following header checks needed for bzero in Storage.m and other places
|
||||
#--------------------------------------------------------------------
|
||||
# Autoupdate added the next two lines to ensure that your configure
|
||||
# script's behavior did not change. They are probably safe to remove.
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
|
||||
printf %s "checking for egrep... " >&6; }
|
||||
if test ${ac_cv_path_EGREP+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
|
||||
then ac_cv_path_EGREP="$GREP -E"
|
||||
else
|
||||
if test -z "$EGREP"; then
|
||||
ac_path_EGREP_found=false
|
||||
# Loop through the user's path and test for each of PROGNAME-LIST
|
||||
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
|
||||
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
|
||||
do
|
||||
IFS=$as_save_IFS
|
||||
case $as_dir in #(((
|
||||
'') as_dir=./ ;;
|
||||
*/) ;;
|
||||
*) as_dir=$as_dir/ ;;
|
||||
esac
|
||||
for ac_prog in egrep
|
||||
do
|
||||
for ac_exec_ext in '' $ac_executable_extensions; do
|
||||
ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext"
|
||||
as_fn_executable_p "$ac_path_EGREP" || continue
|
||||
# Check for GNU ac_path_EGREP and select it if it is found.
|
||||
# Check for GNU $ac_path_EGREP
|
||||
case `"$ac_path_EGREP" --version 2>&1` in
|
||||
*GNU*)
|
||||
ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
|
||||
*)
|
||||
ac_count=0
|
||||
printf %s 0123456789 >"conftest.in"
|
||||
while :
|
||||
do
|
||||
cat "conftest.in" "conftest.in" >"conftest.tmp"
|
||||
mv "conftest.tmp" "conftest.in"
|
||||
cp "conftest.in" "conftest.nl"
|
||||
printf "%s\n" 'EGREP' >> "conftest.nl"
|
||||
"$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
|
||||
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
|
||||
as_fn_arith $ac_count + 1 && ac_count=$as_val
|
||||
if test $ac_count -gt ${ac_path_EGREP_max-0}; then
|
||||
# Best one so far, save it but keep looking for a better one
|
||||
ac_cv_path_EGREP="$ac_path_EGREP"
|
||||
ac_path_EGREP_max=$ac_count
|
||||
fi
|
||||
# 10*(2^10) chars as input seems more than enough
|
||||
test $ac_count -gt 10 && break
|
||||
done
|
||||
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
|
||||
esac
|
||||
|
||||
$ac_path_EGREP_found && break 3
|
||||
done
|
||||
done
|
||||
done
|
||||
IFS=$as_save_IFS
|
||||
if test -z "$ac_cv_path_EGREP"; then
|
||||
as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
|
||||
fi
|
||||
else
|
||||
ac_cv_path_EGREP=$EGREP
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
|
||||
printf "%s\n" "$ac_cv_path_EGREP" >&6; }
|
||||
EGREP="$ac_cv_path_EGREP"
|
||||
|
||||
|
||||
|
||||
ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_string_h" = xyes
|
||||
|
@ -14893,6 +14821,58 @@ fi
|
|||
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Check dor 'dot', needed for graphs in autogsdoc output.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
|
||||
# Extract the first word of "dot", so it can be a program name with args.
|
||||
set dummy dot; ac_word=$2
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
|
||||
printf %s "checking for $ac_word... " >&6; }
|
||||
if test ${ac_cv_path_DOT+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
case $DOT in
|
||||
[\\/]* | ?:[\\/]*)
|
||||
ac_cv_path_DOT="$DOT" # Let the user override the test with a path.
|
||||
;;
|
||||
*)
|
||||
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
|
||||
for as_dir in $PATH
|
||||
do
|
||||
IFS=$as_save_IFS
|
||||
case $as_dir in #(((
|
||||
'') as_dir=./ ;;
|
||||
*/) ;;
|
||||
*) as_dir=$as_dir/ ;;
|
||||
esac
|
||||
for ac_exec_ext in '' $ac_executable_extensions; do
|
||||
if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
|
||||
ac_cv_path_DOT="$as_dir$ac_word$ac_exec_ext"
|
||||
printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
IFS=$as_save_IFS
|
||||
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
DOT=$ac_cv_path_DOT
|
||||
if test -n "$DOT"; then
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DOT" >&5
|
||||
printf "%s\n" "$DOT" >&6; }
|
||||
else
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
printf "%s\n" "no" >&6; }
|
||||
fi
|
||||
|
||||
|
||||
HAVE_DOT=$ac_cv_path_DOT
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Check whether nl_langinfo(CODESET) is supported, needed by Unicode.m.
|
||||
|
|
11
configure.ac
11
configure.ac
|
@ -73,7 +73,7 @@ builtin(include, config/addlibrarypath.m4)dnl
|
|||
builtin(include, config/pkg.m4)dnl
|
||||
|
||||
AC_INIT
|
||||
AC_PREREQ([2.60])
|
||||
AC_PREREQ([2.71])
|
||||
AC_CONFIG_SRCDIR([Source/NSArray.m])
|
||||
|
||||
# If GNUSTEP_MAKEFILES is undefined, try to use gnustep-config to determine it.
|
||||
|
@ -2346,7 +2346,7 @@ AC_C_INLINE
|
|||
#--------------------------------------------------------------------
|
||||
# Following header checks needed for bzero in Storage.m and other places
|
||||
#--------------------------------------------------------------------
|
||||
AC_HEADER_STDC
|
||||
|
||||
AC_CHECK_HEADERS(string.h memory.h alloca.h)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
|
@ -3838,6 +3838,13 @@ fi
|
|||
|
||||
AC_SUBST(USE_GMP)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Check dor 'dot', needed for graphs in autogsdoc output.
|
||||
#--------------------------------------------------------------------
|
||||
AC_ARG_VAR([DOT], [The name or full path of the graphviz dot command.])
|
||||
|
||||
AC_PATH_PROG([DOT], [dot], [])
|
||||
AC_SUBST(HAVE_DOT, [$ac_cv_path_DOT])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Check whether nl_langinfo(CODESET) is supported, needed by Unicode.m.
|
||||
|
|
Loading…
Reference in a new issue