Skip to content

Commit

Permalink
Merge pull request #66 from lbl-srg/issue64_headlessLinux
Browse files Browse the repository at this point in the history
Issue64 headless linux
  • Loading branch information
JayHuLBL authored Sep 6, 2023
2 parents 5ea4ef9 + 342efde commit 14b485f
Show file tree
Hide file tree
Showing 16 changed files with 106 additions and 75 deletions.
21 changes: 13 additions & 8 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@ jobs:

strategy:
matrix:
os: [ubuntu-18.04, ubuntu-latest, windows-latest, macos-latest]
python-version: ['2.7', '3.8']
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9']

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Enable Developer Command Prompt
uses: ilammy/[email protected]
if: startsWith(matrix.os, 'windows')

- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.8
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.19.x'
cmake-version: '3.22.x'

- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

Expand Down Expand Up @@ -62,11 +66,12 @@ jobs:
run: ctest -C $BUILD_TYPE --verbose

- name: Push changes to remote
if: contains(matrix.os, 'latest') && contains(matrix.python-version, '3.8')
if: contains(matrix.os, 'latest') && contains(matrix.python-version, '3.9')
run: |
git config --global user.name $GH_USERNAME
git config --global user.email [email protected]
git config --global pull.rebase false
git add ${{ github.workspace }}/pyfunnel/lib
git commit -m "Add binaries" --allow-empty
git commit -m "Add ${{ matrix.os }} binaries" --allow-empty
git pull
git push
31 changes: 15 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#

cmake_policy(SET CMP0048 NEW)
cmake_minimum_required(VERSION 3.11...3.19)
cmake_minimum_required(VERSION 3.22)
file(READ "${CMAKE_SOURCE_DIR}/pyfunnel/VERSION" VERSION)
project(funnel VERSION ${VERSION} LANGUAGES C)
enable_testing()
Expand All @@ -34,17 +34,15 @@ else()
endif()

# Set compiling and linking options.
set(UNIX_COMPILE_FLAGS "-Wall -Wextra -Werror -Wfloat-equal -Wpedantic -o3 -fPIC")
set(WIN_COMPILE_FLAGS "/Wall /O2")
set(UNIX_LINK_FLAGS "-fPIC")

if(LINUX OR MACOSX) # CMake 3.13.*: support for add_compile_options and add_link_options
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${UNIX_COMPILE_FLAGS}")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${UNIX_LINK_FLAGS}")
if(LINUX OR MACOSX)
set(CMAKE_C_FLAGS "-fPIC -Wall -Wextra -Werror -Wfloat-equal -Wpedantic -O3")
set(CMAKE_SHARED_LINKER_FLAGS "-fPIC")
else()
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WIN_COMPILE_FLAGS}")
set(CMAKE_C_FLAGS "/Wall /O2")
endif()

message("CMAKE_C_FLAGS=${CMAKE_C_FLAGS}")

# Set target directories.
# NOTE: always add quotes to protect spaces in path when setting new variables.
if (WINDOWS)
Expand Down Expand Up @@ -81,7 +79,8 @@ add_subdirectory(tests)
############################################################

# Manage tests.
include(FindPythonInterp)
find_package (Python)
message("Python interpreter used for testing: ${Python_EXECUTABLE}")
set(CTEST_OUTPUT_ON_FAILURE ON) # verbose if failed test
set(CMAKE_TEST_DIR "${CMAKE_SOURCE_DIR}/tests")

Expand All @@ -102,22 +101,22 @@ add_test(
set(TEST_ARGS_0 --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002 --output results)
add_test(
NAME test_py_0
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_0}
COMMAND ${Python_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_0}
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
### Wrong arguments
set(TEST_ARGS_1 --reference wrong.csv --test simulated.csv --atolx 0.002 --atoly 0.002 --output results)
add_test(
NAME test_py_1
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_1}
COMMAND ${Python_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_1}
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
set_tests_properties(test_py_1 PROPERTIES PASS_REGULAR_EXPRESSION "No such file")
### Wrong arguments
set(TEST_ARGS_2 --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002)
add_test(
NAME test_py_2
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_2}
COMMAND ${Python_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_2}
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
set_tests_properties(test_py_2 PROPERTIES PASS_REGULAR_EXPRESSION "Output directory not specified")
Expand All @@ -142,7 +141,7 @@ foreach(t RANGE ${len_tests})
)
add_test(
NAME ${test_name}
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_TEST_DIR}/test_numerics.py" ${test_name} ${test_dir} ${tmp_dir}
COMMAND ${Python_EXECUTABLE} "${CMAKE_TEST_DIR}/test_numerics.py" ${test_name} ${test_dir} ${tmp_dir}
WORKING_DIRECTORY ${tmp_dir}
)
if (${test_case} MATCHES "fail4") # WILL_FAIL shall not be used simultaneously with PASS_REGULAR_EXPRESSION!
Expand All @@ -156,15 +155,15 @@ endforeach()
## Plot function testing.
add_test(
NAME test_plot
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_TEST_DIR}/test_plot.py" "${CMAKE_TEST_DIR}/fail1/results"
COMMAND ${Python_EXECUTABLE} "${CMAKE_TEST_DIR}/test_plot.py" "${CMAKE_TEST_DIR}/fail1/results"
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
set_tests_properties(test_plot PROPERTIES FAIL_REGULAR_EXPRESSION "Exception;Error;Traceback")

## Configure pre/post test.
set(summary_path "${CMAKE_TEST_DIR}/test_summary.py")
set(CTEST_CUSTOM_POST_TEST
"${PYTHON_EXECUTABLE} ${summary_path} ${CMAKE_TEST_DIR}"
"${Python_EXECUTABLE} ${summary_path} ${CMAKE_TEST_DIR}"
)
set(content "# Numerics testing configuration file
set(CTEST_CUSTOM_PRE_TEST
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The software is tested on the following platforms.
* Windows x64
* macOS

A Python binding is available to access the library. It is compatible with Python 2 and 3.
A Python binding is available to access the library. It is supported on Python versions 3.8 through 3.9.

### Installing

Expand Down Expand Up @@ -140,7 +140,7 @@ $ python ../../pyfunnel/pyfunnel.py --reference trended.csv --test simulated.csv

### System Requirements

The cross-platform build system relies on CMake (version between `3.11.*` and `3.19.*`).
The cross-platform build system relies on CMake (version `3.22`).

The distributed binaries are built with Microsoft Visual Studio C/C++ compiler
(Windows), Clang (macOS) and GCC (Linux).
Expand Down
75 changes: 50 additions & 25 deletions pyfunnel/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@
BROWSER=None,
)

# Get the real browser name in case webbrowser.get(browser).name returns 'xdg-open' on Ubuntu.
# Get the real browser name in case webbrowser.get(browser).name returns 'xdg-open' on Linux.
# This is only for guarding against Chromium bug (excess logging on /var/log/syslog).
# The browser name retrieved in LINUX_DEFAULT is not compatible with browser names from the webbrowser module.
LINUX_DEFAULT = None
if platform.system() == 'Linux':
try:
LINUX_DEFAULT = str(subprocess.check_output('xdg-settings get default-web-browser', shell=True))
LINUX_DEFAULT = str(subprocess.check_output('xdg-settings get default-web-browser 2>/dev/null', shell=True))
except BaseException:
pass

Expand Down Expand Up @@ -359,10 +361,10 @@ def server_close(self):
print('Could not close logger: {}'.format(e))

def browse(self, *args, **kwargs):
"""Launch server and web browser.
"""Launch server, and web browser if available.
kwargs:
browser (str): name of browser, see https://docs.python.org/3.8/library/webbrowser.html
browser (str): name of browser, see https://docs.python.org/3/library/webbrowser.html
timeout (float): maximum time (s) before server shutdown
"""
global CONFIG
Expand All @@ -373,44 +375,68 @@ def browse(self, *args, **kwargs):
cmd = 'import webbrowser; webbrowser.get().open("http://localhost:{}/funnel")'.format(
self.server_port
)
launch_browser = True
if browser is None:
# This assignment cannot be done with browser = kwargs.pop('browser', CONFIG['BROWSER'])
# as another module can call browse(browser=None).
# browser may still be None after this assignment, which means "use default browser".
browser = CONFIG['BROWSER']
# We now test for the existence of a GUI browser.
try:
browser_ctrl = webbrowser.get(browser)
if 'BackgroundBrowser' in str(browser_ctrl.__class__):
launch_browser = False
except webbrowser.Error:
launch_browser = False

# Create command to launch browser in subprocess.
cmd = 'import webbrowser; webbrowser.get().open("http://localhost:{}/funnel")'.format(
self.server_port
)
# Add browser name within quotes but only if not None.
if browser is not None:
webbrowser.get(browser) # Throw exception in case of missing browser.
# Pass browser name within quotes.
cmd = re.sub(r'get\(\)', 'get("{}")'.format(browser), cmd)
webbrowser_cmd = [sys.executable, '-c', cmd]
# Move to directory with *.csv before launching local server.
cur_dir = os.getcwd()
os.chdir(self._BROWSE_DIR)

try:
self.server_launch()
# Launch browser as a subprocess command to avoid web browser error into terminal.
with open(os.devnull, 'w') as pipe:
proc = subprocess.Popen(webbrowser_cmd, stdout=pipe, stderr=pipe)
if launch_browser:
with open(os.devnull, 'w') as pipe:
proc = subprocess.Popen(webbrowser_cmd, stdout=pipe, stderr=pipe)
# Watch syslog for error.
chrome_error = False
if platform.system() == 'Linux':
if (browser is None and 'chrome' in LINUX_DEFAULT) or (
browser is not None and 'chrome' in browser):
with open('/var/log/syslog') as f:
for l in follow(f, 2):
if 'ERROR:gles2_cmd_decoder' in l:
chrome_error = True
break
if (browser is None and LINUX_DEFAULT is not None and 'chrome' in LINUX_DEFAULT) or (
browser is not None and 'chrome' in browser):
log_path = '/var/log/syslog' # Ubuntu
if not os.path.exists(log_path):
log_path = '/var/log/messages' # RHEL/CentOS
if not os.path.exists(log_path):
log_path = None
if log_path is not None:
with open(log_path) as f:
for l in follow(f, 2):
if 'ERROR:gles2_cmd_decoder' in l:
chrome_error = True
break
if chrome_error:
proc.terminate() # Terminating the process does not stop Chrome in background.
subprocess.check_call(['pkill', 'chrome']) # This does.
inp = 'y'
while True: # Prompt user to retry.
inp = input(
('Launching browser yields syslog errors, '
'probably because Chrome is used and the display entered screensaver mode.\n'
'All related processes have been killed by precaution.\n'
'If you have Firefox installed and want to use it persistently, enter p\n'
'Otherwise, do you simply want to retry ([y]/n/p)? '))
(
'Launching browser yields syslog errors, '
'probably because Chrome is used and the display entered screensaver mode.\n'
'All related processes have been killed by precaution.\n'
'If you have Firefox installed and want to use it persistently, enter p\n'
'Otherwise, do you simply want to retry ([y]/n/p)? '
)
)
if inp not in ['y', 'n', 'p']:
continue
else:
Expand All @@ -419,7 +445,7 @@ def browse(self, *args, **kwargs):
# Current module for future calls to the function.
CONFIG['BROWSER'] = 'firefox'
save_config() # Configuration file for future imports.
browser = 'firefox' # Current function for immediate retry.
browser = 'firefox' # Use firefox browser for immediate retry.
cmd = re.sub(r'get\(.*?\)', 'get("{}")'.format(browser), cmd)
webbrowser_cmd = [sys.executable, '-c', cmd]
if inp == 'y' or inp == 'p':
Expand All @@ -429,13 +455,12 @@ def browse(self, *args, **kwargs):
proc = subprocess.Popen(webbrowser_cmd, stdout=pipe, stderr=pipe)
else:
raise KeyboardInterrupt
if timeout > 10: # Do not pollute terminal if HTML page is served only for a short time.
print('Server will run for {} (s) or until KeyboardInterrupt.'.format(timeout))

print('Server will run for {} s (or until KeyboardInterrupt) at:\n'.format(timeout) + \
'http://localhost:{}/funnel'.format(self.server_port))
wait_until(exit_test, timeout, 0.1, self.logger, *args)
except KeyboardInterrupt:
print('KeyboardInterrupt')
except Exception as e:
print(e)
finally:
os.chdir(cur_dir)
try: # Objects may not be defined in case of exception.
Expand Down
Binary file modified pyfunnel/lib/darwin64/libfunnel.dylib
Binary file not shown.
Binary file modified pyfunnel/lib/linux64/libfunnel.so
Binary file not shown.
Binary file modified pyfunnel/lib/win64/funnel.dll
Binary file not shown.
Binary file modified pyfunnel/lib/win64/funnel.lib
Binary file not shown.
6 changes: 2 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
six==1.14.0

# Test and documentation
pandas==1.2.4; python_version>='3.6'
pandas==0.24.2; python_version~='2.7'
setuptools==46.0.0; python_version>='3.6'
setuptools==44.0.0; python_version~='2.7'
pandas==1.2.4; python_version>='3.8'
setuptools==46.0.0; python_version>='3.8'
wheel
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,15 @@
long_description=README,
long_description_content_type='text/markdown',
license="3-clause BSD",
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
python_requires='>=3.8',
install_requires=['six>=1.11'],
packages=[MAIN_PACKAGE],
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.8',
'Programming Language :: C',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Science/Research',
Expand Down
2 changes: 1 addition & 1 deletion src/algorithmRectangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
* --------------------
* creates new linked list node
*/
node_t * createNode() {
node_t * createNode(void) {
node_t* temp;
temp = malloc(sizeof(node_t));
if (temp == NULL){
Expand Down
2 changes: 1 addition & 1 deletion src/algorithmRectangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ typedef struct node {
struct node * next;
} node_t;

node_t * createNode();
node_t * createNode(void);

node_t * addNode(node_t* head, double newVal);

Expand Down
7 changes: 7 additions & 0 deletions src/compare.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
#define equ(a,b) (fabs((a)-(b)) < 1e-10 ? true : false) /* (b) required by Win32 compiler for <0 values */
#endif

/*
* Descriptor of the file used for logging the numerical processing errors
* (all other errors like memory, file access, bad argument...
* are still output to stderr.)
*/
FILE *log_file;

/*
* Function: buildPath
* -----------------------
Expand Down
7 changes: 0 additions & 7 deletions src/compare.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@

#define MAX 100

/*
* Descriptor of the file used for logging the numerical processing errors
* (all other errors like memory, file access, bad argument...
* are still output to stderr.)
*/
FILE *log_file;

/*
* Function: compareAndReport
* -----------------------
Expand Down
Loading

0 comments on commit 14b485f

Please sign in to comment.