Skip to content

Commit a375ca4

Browse files
committed
Fix macOS app build.
Required some tweaks to the build to get Xcode archiving to work properly. Also, made a failure to set the user data directory, for the batch tool, to be a warning, and not an error (this should probably be fixed at some point, if it can be).
1 parent 1fe57a5 commit a375ca4

File tree

8 files changed

+4662
-28
lines changed

8 files changed

+4662
-28
lines changed

CMakeLists.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,8 @@ if( BUILD_AS_OSX_APP )
768768
MACOSX_FRAMEWORK_SHORT_VERSION_STRING 1.0
769769
MACOSX_FRAMEWORK_BUNDLE_VERSION 1.0.0
770770
MACOSX_FRAMEWORK_COPYRIGHT "© 2024 Sandia National Laboratories"
771+
# XCODE_ATTRIBUTE_INSTALL_PATH @executable_path/../Frameworks/
772+
XCODE_ATTRIBUTE_SKIP_INSTALL "Yes"
771773
)
772774

773775
set_target_properties( InterSpecLib PROPERTIES
@@ -837,10 +839,10 @@ if(TRY_TO_STATIC_LINK)
837839
target_link_libraries(InterSpecLib PUBLIC ${FCGI_LIBRARIES})
838840
endif(Wt_FCGI_LIBRARY)
839841

840-
include(cmake/WtFindSsl.txt)
841-
if(SSL_FOUND)
842-
target_link_libraries(InterSpecLib PUBLIC ${SSL_LIBRARIES})
843-
endif(SSL_FOUND)
842+
#include(cmake/WtFindSsl.txt)
843+
#if(SSL_FOUND)
844+
# target_link_libraries(InterSpecLib PUBLIC ${SSL_LIBRARIES})
845+
#endif(SSL_FOUND)
844846

845847
if(("${CMAKE_SYSTEM}" MATCHES "Linux") AND (NOT ANDROID))
846848
find_library( RT_LIB rt REQUIRES )

InterSpec_resources/app_text/InterSpec_strings.csv

Lines changed: 2220 additions & 0 deletions
Large diffs are not rendered by default.

InterSpec_resources/app_text/InterSpec_strings_git_ff5d1e241.csv

Lines changed: 2220 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import csv
2+
from xml.etree.ElementTree import Element, SubElement, Comment, tostring, fromstring, ParseError
3+
from xml.dom import minidom
4+
import os
5+
6+
# Define the path to your CSV file
7+
csv_file_path = 'InterSpec_strings.csv'
8+
9+
# Define a directory to store the output XML files
10+
output_dir = 'converted_xml_output'
11+
os.makedirs(output_dir, exist_ok=True)
12+
13+
# A dictionary to hold XML data grouped by file_name
14+
xml_data = {}
15+
16+
# Function to safely parse XHTML content
17+
def parse_xhtml(xhtml_str):
18+
try:
19+
# Wrap content in a div to ensure a single root element
20+
wrapped_content = f"<div>{xhtml_str}</div>"
21+
return fromstring(wrapped_content)
22+
23+
dom = fromstring(wrapped_content)
24+
children_txt = ''.join(tostring(child, encoding='unicode') for child in dom)
25+
if dom.text is not None:
26+
if "Tool to fit for" in xhtml_str:
27+
print( f"xhtml_str={xhtml_str}")
28+
print( f"dom.txt={dom.text}, and child join: {children_txt}")
29+
return dom.text + children_txt
30+
else:
31+
if "Tool to fit for" in xhtml_str:
32+
print( f"No DOM txt")
33+
return children_txt
34+
except ParseError:
35+
# Handle or log parsing errors here
36+
return None
37+
38+
# Read the CSV file
39+
with open(csv_file_path, mode='r', newline='', encoding='utf-8-sig') as file:
40+
reader = csv.DictReader(file)
41+
42+
# Process each row in the CSV
43+
for row in reader:
44+
file_name = row['XML Filename']
45+
if file_name not in xml_data:
46+
xml_data[file_name] = []
47+
xml_data[file_name].append(row)
48+
49+
# Generate XML files
50+
for file_name, messages in xml_data.items():
51+
# Create the root element
52+
root = Element('messages')
53+
54+
for msg in messages:
55+
# Create a message element
56+
if msg['id']:
57+
message_elem = SubElement(root, 'message')
58+
message_elem.set('id', msg['id'])
59+
if msg['Comment']:
60+
message_elem.set('comment', msg['Comment'])
61+
# Parse and append XHTML content
62+
xhtml_content = parse_xhtml(msg['English Value'])
63+
#print( tostring(xhtml_content, 'utf-8').decode("utf-8") )
64+
if xhtml_content is not None:
65+
message_elem.text = xhtml_content.text
66+
# Since we wrapped content in a div, we iterate over its children
67+
for child in xhtml_content:
68+
message_elem.append(child)
69+
#message_elem.text = xhtml_content
70+
#message_elem.text = msg['English Value']
71+
else:
72+
# Add XML comment if present
73+
#if msg['Comment']:
74+
# root.append(Comment(msg['English Value']))
75+
# comments dont seem to be coming through very well, so we'll do a hack
76+
message_elem = SubElement(root, 'CommentPlaceholder')
77+
message_elem.text = msg['English Value']
78+
79+
# Convert the ElementTree to a string
80+
xml_str = tostring(root, 'utf-8', "xml")
81+
pretty_xml_str = minidom.parseString(xml_str).toprettyxml(indent=" ")
82+
83+
pretty_xml_str = pretty_xml_str.replace('<CommentPlaceholder>', '<!--' )
84+
pretty_xml_str = pretty_xml_str.replace('</CommentPlaceholder>', '-->' )
85+
pretty_xml_str = pretty_xml_str.replace('&quot;', '"' )
86+
87+
88+
# Write to an XML file
89+
with open(os.path.join(output_dir, file_name), 'w', encoding='utf-8') as xml_file:
90+
xml_file.write(pretty_xml_str)
91+
#xml_file.write(xml_str.decode("utf-8"))
92+
93+
94+
print("CSV has been converted back into XML documents.")
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import csv
2+
import xml.etree.ElementTree as ET
3+
import os
4+
import glob
5+
6+
# Define the path for the output CSV file
7+
csv_file_path = 'InterSpec_strings.csv'
8+
9+
class CommentedTreeBuilder(ET.TreeBuilder):
10+
def comment(self, data):
11+
self.start(ET.Comment, {})
12+
self.data(data)
13+
self.end(ET.Comment)
14+
15+
# Function to extract XHTML content from an element
16+
def get_xhtml_content(element):
17+
# Serialize the element's children to a string and decode bytes to string
18+
return ''.join(ET.tostring(child, encoding='unicode') for child in element)
19+
20+
21+
# Open a file for writing with UTF-8 encoding and include the BOM
22+
with open(csv_file_path, mode='w', newline='', encoding='utf-8-sig') as file:
23+
writer = csv.writer(file)
24+
25+
# Write the header row
26+
writer.writerow(['XML Filename', 'id', 'Comment', 'English Value'])
27+
28+
# We could loop over files in a whatever order glob.glob('*.xml') gives us
29+
# But lets instead put the files in an order so there is a tiny bit more context
30+
xml_files = [ 'InterSpecApp.xml', 'InterSpec.xml', 'D3SpectrumDisplayDiv.xml', 'CompactFileManager.xml', \
31+
'ReferencePhotopeakDisplay.xml', 'PeakInfoDisplay.xml', 'PeakModel.xml', 'EnergyCalTool.xml', \
32+
'IsotopeSearchByEnergy.xml', 'DrfSelect.xml', 'ShieldingSelect.xml', \
33+
'ShieldingSourceDisplay.xml', 'GammaCountDialog.xml', 'MultimediaDisplay.xml', \
34+
'MakeFwhmForDrf.xml', 'SearchMode3DChart.xml', 'SpecMeasManager.xml', \
35+
'FeatureMarkerWidget.xml', 'SpecFileSummary.xml', 'LeafletRadMap.xml', \
36+
'DecayActivity.xml', 'GammaXsGui.xml', 'HelpSystem.xml', 'OneOverR2Calc.xml', 'RemoteRid.xml', \
37+
'MakeDrf.xml', 'UnitsConverterTool.xml', 'FluxTool.xml', 'DoseCalcWidget.xml', \
38+
'ShowRiidInstrumentsAna.xml', 'RelActManualGui.xml', 'LicenseAndDisclaimersWindow.xml' \
39+
]
40+
41+
# But lets check to make sure the above array contains all XML files we want
42+
43+
# Loop over all XML files in the current directory
44+
for xml_file_path in glob.glob('*.xml'):
45+
# Extract the file name without the path
46+
xml_file_name = os.path.basename(xml_file_path)
47+
48+
# Skip files with "_" in their names
49+
if "_" in xml_file_name:
50+
continue
51+
52+
if xml_file_name not in xml_files:
53+
print( "Missing file ", xml_file_name, " from `xml_files` array" )
54+
exit(-1)
55+
56+
# Now we can start processing XML file.
57+
for xml_file_path in xml_files:
58+
xml_file_name = xml_file_path
59+
print( "Working on ", xml_file_name )
60+
61+
# Parse the XML file
62+
try:
63+
parser = ET.XMLParser(target=CommentedTreeBuilder())
64+
tree = ET.parse(xml_file_path, parser)
65+
except ET.ParseError as e:
66+
print(f"Error parsing {xml_file_name}: {e}")
67+
continue # Skip to the next file if an error occurs
68+
69+
root = tree.getroot()
70+
71+
# Iterate over each node in the XML
72+
for node in root:
73+
# Check if the node is a 'message' element
74+
if node.tag == 'message':
75+
# Extract the 'id' attribute, text content of the message, and 'comment' attribute if it exists
76+
id_attr = node.get('id')
77+
78+
message_text = node.text
79+
80+
# Extract XHTML content
81+
message_xhtml = get_xhtml_content(node)
82+
if len(message_xhtml) > 1:
83+
if not message_text:
84+
message_text = ''
85+
message_text = message_text + message_xhtml
86+
87+
element_comment = node.get('comment', '') # Default to empty string if 'comment' attribute does not exist
88+
89+
# Write the extracted data to the CSV file, including the element's 'comment' attribute and the last seen XML comment
90+
writer.writerow([xml_file_name, id_attr, element_comment, message_text])
91+
92+
# Check if the node is a comment
93+
if node.tag is ET.Comment:
94+
# Write the comment in the next line with empty values for other columns, except for the comment column
95+
writer.writerow([xml_file_name, '', '', node.text])
96+
97+
print("Conversion to CSV completed successfully.")

external_libs/SpecUtils

target/batch/batch_main.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ int main( int argc, char *argv[] )
178178
{
179179
#if( BUILD_AS_OSX_APP )
180180
user_data_dir = macOsUtils::user_data_dir();
181+
if( !SpecUtils::is_directory(user_data_dir) )
182+
{
183+
user_data_dir = "";
184+
std::cerr << "Warning: not setting user data directory, to what the GUI version of InterSpec"
185+
" uses - use the '--userdatadir' option to set this if you would like to share detectors,"
186+
" or other files." << std::endl;
187+
}
181188
#elif defined(_WIN32)
182189
user_data_dir = AppUtils::user_data_dir();
183190
#else
@@ -194,8 +201,10 @@ should fix this for linux
194201
InterSpec::setWritableDataDirectory( user_data_dir );
195202
}catch( std::exception &e )
196203
{
197-
std::cerr << "Failed to set user data directory: " << e.what() << std::endl;
198-
return -8;
204+
std::cerr << "Warning: Failed to set user data directory: " << e.what() << std::endl
205+
<< "Use the '--userdatadir' option to set this to the same one the InterSpec GUI app"
206+
<< " uses, if you would like to share detectors, or other files." << std::endl;
207+
//return -8;
199208
}
200209

201210
try

target/osx/CMakeLists.txt

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ SET(MACOSX_BUNDLE_BUNDLE_VERSION "0" )
2828
SET(MACOSX_BUNDLE_COPYRIGHT "Sandia National Labs, Will Johnson" )
2929
SET(MACOSX_BUNDLE_GUI_IDENTIFIER "gov.sandia.macOS.InterSpec" )
3030
#set MACOS_BUNDLE_VERSION_NUMBER for Info.plist.template to populate corresponding value in Xcode
31-
set(MACOS_BUNDLE_VERSION_NUMBER 44)
31+
set(MACOS_BUNDLE_VERSION_NUMBER 45)
3232

3333

3434
SET( ${PRODUCT_NAME} "InterSpec" )
@@ -126,34 +126,26 @@ add_dependencies(InterSpecApp SpecFilePreview)
126126
if( USE_BATCH_TOOLS )
127127
add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/../batch InterSpec_batch )
128128
add_dependencies( InterSpecApp InterSpec_batch )
129-
endif( USE_BATCH_TOOLS )
130-
131-
132-
# Make sure the Frameworks directory exists
133-
add_custom_command(TARGET InterSpecApp POST_BUILD
134-
COMMAND ${CMAKE_COMMAND} -E make_directory
135-
$<TARGET_BUNDLE_DIR:InterSpecApp>/Contents/Frameworks
136-
)
137-
138-
add_custom_command(TARGET InterSpecApp POST_BUILD
139-
COMMAND ${CMAKE_COMMAND} -E copy_directory
140-
$<TARGET_BUNDLE_DIR:InterSpecLib>
141-
$<TARGET_BUNDLE_DIR:InterSpecApp>/Contents/Frameworks/InterSpec.framework
142-
)
129+
130+
# It seems like $<TARGET_BUNDLE_DIR:InterSpecLib> is a symlink to the actual build location, so we
131+
# need to copy the symlinks path, but also, preserve symlinks inside the bundle. So we'll use
132+
# Unix cp to do this, after getting where the symlink points to
133+
add_custom_command(TARGET InterSpecApp POST_BUILD
134+
COMMAND mkdir -p $<TARGET_BUNDLE_DIR:InterSpecApp>/Contents/Frameworks && cp -a `readlink -f $<TARGET_BUNDLE_DIR:InterSpecLib>` $<TARGET_BUNDLE_DIR:InterSpecApp>/Contents/Frameworks/InterSpec.framework
135+
)
143136

144-
# We want to look in InterSpec.app/Contents/Frameworks for InterSpecLib to link to
145-
set_target_properties( InterSpecApp PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks" )
146-
set_target_properties( InterSpecApp PROPERTIES BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@rpath" )
137+
# We want to look in InterSpec.app/Contents/Frameworks for InterSpecLib to link to
138+
set_target_properties( InterSpecApp PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks" )
139+
set_target_properties( InterSpecApp PROPERTIES BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@rpath" )
147140

148-
if( USE_BATCH_TOOLS )
149141
set_target_properties( InterSpec_batch PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks" )
150142
set_target_properties( InterSpec_batch PROPERTIES BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@rpath" )
151143

152-
# Copy the batch executable into the bundle
144+
# Copy the batch executable into the bundle - not really sure where is best, so we'll put it in Resources right now
153145
add_custom_command(TARGET InterSpecApp POST_BUILD
154146
COMMAND ${CMAKE_COMMAND} -E copy
155147
$<TARGET_FILE:InterSpec_batch>
156-
$<TARGET_FILE_DIR:InterSpecApp>
148+
$<TARGET_FILE_DIR:InterSpecApp>/
157149
)
158150
endif( USE_BATCH_TOOLS )
159151

0 commit comments

Comments
 (0)