diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 4d253c2e..ba293ee4 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -24,6 +24,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Synchronize Node.js binding version + run: | + echo "Synchronizing Node.js binding version with main project..." + python3 scripts/sync_node_version.py + - name: Get package version id: package working-directory: libCacheSim-node @@ -81,6 +86,11 @@ jobs: with: fetch-depth: 0 + - name: Synchronize Node.js binding version + run: | + echo "Synchronizing Node.js binding version with main project..." + python3 scripts/sync_node_version.py + - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -140,6 +150,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Synchronize Node.js binding version + run: | + echo "Synchronizing Node.js binding version with main project..." + python3 scripts/sync_node_version.py + - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f57cb78..a89d8ead 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,11 @@ project(libCacheSim) set(DESCRIPTION "a high performance cache simulation library") set(PROJECT_WEB "http://cachesim.com") -set(${PROJECT_NAME}_VERSION_MAJOR 0) -set(${PROJECT_NAME}_VERSION_MINOR 1) -set(${PROJECT_NAME}_VERSION_PATCH 0) -set(${PROJECT_NAME}_RELEASE_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}) -set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_RELEASE_VERSION}.${${PROJECT_NAME}_VERSION_PATCH}) +# Include version utilities +include(cmake/Version.cmake) + +# Setup version from version.txt +setup_project_version(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/version.txt") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED On) diff --git a/cmake/Version.cmake b/cmake/Version.cmake index dff6d44d..a9f3a5c5 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -1,11 +1,51 @@ +# Version parsing utilities for libCacheSim +# Based on xgboost version handling -# from xgboost -function (write_version) - message(STATUS "xgboost VERSION: ${xgboost_VERSION}") - configure_file( - ${xgboost_SOURCE_DIR}/include/config.h.in - ${xgboost_SOURCE_DIR}/include/config.h @ONLY) +function(parse_version_from_file VERSION_FILE VERSION_MAJOR_VAR VERSION_MINOR_VAR VERSION_PATCH_VAR) + if(EXISTS "${VERSION_FILE}") + file(READ "${VERSION_FILE}" VERSION_STRING) + string(STRIP "${VERSION_STRING}" VERSION_STRING) + + # Parse MAJOR.MINOR.PATCH format + string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" VERSION_MATCH "${VERSION_STRING}") + if(VERSION_MATCH) + set(${VERSION_MAJOR_VAR} ${CMAKE_MATCH_1} PARENT_SCOPE) + set(${VERSION_MINOR_VAR} ${CMAKE_MATCH_2} PARENT_SCOPE) + set(${VERSION_PATCH_VAR} ${CMAKE_MATCH_3} PARENT_SCOPE) + message(STATUS "Version from ${VERSION_FILE}: ${VERSION_STRING}") + else() + message(FATAL_ERROR "Invalid version format in ${VERSION_FILE}: ${VERSION_STRING}. Expected format: MAJOR.MINOR.PATCH") + endif() + else() + message(FATAL_ERROR "Version file not found: ${VERSION_FILE}") + endif() +endfunction() + +function(setup_project_version PROJECT_NAME VERSION_FILE) + if(EXISTS "${VERSION_FILE}") + parse_version_from_file("${VERSION_FILE}" VERSION_MAJOR VERSION_MINOR VERSION_PATCH) + set(${PROJECT_NAME}_VERSION_MAJOR ${VERSION_MAJOR} PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_MINOR ${VERSION_MINOR} PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_PATCH ${VERSION_PATCH} PARENT_SCOPE) + set(${PROJECT_NAME}_RELEASE_VERSION ${VERSION_MAJOR}.${VERSION_MINOR} PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH} PARENT_SCOPE) + else() + message(STATUS "${VERSION_FILE} not found, using default version") + set(${PROJECT_NAME}_VERSION_MAJOR 0 PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_MINOR 1 PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_PATCH 0 PARENT_SCOPE) + set(${PROJECT_NAME}_RELEASE_VERSION 0.1 PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION 0.1.0 PARENT_SCOPE) + endif() +endfunction() + +# Legacy function for xgboost compatibility +function(write_version) + message(STATUS "xgboost VERSION: ${xgboost_VERSION}") + configure_file( + ${xgboost_SOURCE_DIR}/include/config.h.in + ${xgboost_SOURCE_DIR}/include/config.h @ONLY) # configure_file( # ${xgboost_SOURCE_DIR}/cmake/Python_version.in # ${xgboost_SOURCE_DIR}/python-package/xgboost/VERSION @ONLY) -endfunction (write_version) +endfunction(write_version) diff --git a/libCacheSim-node/README.md b/libCacheSim-node/README.md index 14d30704..f26d5ca5 100644 --- a/libCacheSim-node/README.md +++ b/libCacheSim-node/README.md @@ -46,6 +46,12 @@ console.log('Simulation results:', result); ## API Reference +### `getVersion()` + +Get the version of the libCacheSim Node.js binding. + +**Returns:** String containing the version number (e.g., "1.0.1") + ### `runSimulation(tracePath, traceType, algorithm, cacheSize)` Run a cache simulation with specified parameters. @@ -100,6 +106,9 @@ npm install -g libcachesim-node # Run simulation from command line cachesim-js --trace /path/to/trace.vscsi --algorithm s3fifo --size 1mb + +# Check version +cachesim-js --version ``` ## Development @@ -132,6 +141,16 @@ npm test - Linux: `build-essential cmake libglib2.0-dev libzstd-dev` - macOS: `cmake glib zstd` (via Homebrew) +### Version Synchronization + +The Node.js binding version is automatically synchronized with the main libCacheSim project version. To manually sync versions, run: + +```bash +python3 scripts/sync_node_version.py +``` + +This ensures that the Node.js binding version in `package.json` matches the version in the main project's `version.txt` file. + ## Architecture This package uses `prebuild-install` for binary distribution: diff --git a/libCacheSim-node/cli.js b/libCacheSim-node/cli.js index 938b9d72..ef13f0b6 100755 --- a/libCacheSim-node/cli.js +++ b/libCacheSim-node/cli.js @@ -11,7 +11,8 @@ function parseArgs() { type: null, algorithm: null, size: null, - help: false + help: false, + version: false }; for (let i = 0; i < args.length; i++) { @@ -35,6 +36,10 @@ function parseArgs() { case '-h': options.help = true; break; + case '--version': + case '-v': + options.version = true; + break; default: console.error(`Unknown option: ${args[i]}`); process.exit(1); @@ -60,6 +65,7 @@ Options: --size, -s Cache size (required) Examples: 1mb, 512kb, 2gb, 1024 (bytes) --help, -h Show this help message + --version, -v Show version information Examples: cachesim-js -t trace.vscsi --type vscsi -a lru -s 10mb @@ -75,6 +81,11 @@ function main() { return; } + if (options.version) { + console.log(`libcachesim-node v${libCacheSim.getVersion()}`); + return; + } + // Check that all required parameters are provided if (!options.trace || !options.type || !options.algorithm || !options.size) { console.error('Error: All parameters are required.'); diff --git a/libCacheSim-node/index.js b/libCacheSim-node/index.js index 4f9ae36a..6ad6acd3 100644 --- a/libCacheSim-node/index.js +++ b/libCacheSim-node/index.js @@ -37,16 +37,30 @@ function getSupportedTraceTypes() { return ['vscsi', 'csv', 'txt', 'binary', 'oracle']; } +/** + * Get the version of the libCacheSim Node.js binding + * @returns {string} Version string + */ +function getVersion() { + try { + const packageJson = require('./package.json'); + return packageJson.version; + } catch (error) { + return 'unknown'; + } +} + module.exports = { runSimulation, runSim, getSupportedAlgorithms, - getSupportedTraceTypes + getSupportedTraceTypes, + getVersion }; // Example usage if run directly if (require.main === module) { - console.log('libCacheSim Node.js Bindings'); + console.log(`libCacheSim Node.js Bindings v${getVersion()}`); console.log('Supported algorithms:', getSupportedAlgorithms()); console.log('Supported trace types:', getSupportedTraceTypes()); diff --git a/libCacheSim-node/package.json b/libCacheSim-node/package.json index 80e60e08..fc06593a 100644 --- a/libCacheSim-node/package.json +++ b/libCacheSim-node/package.json @@ -1,6 +1,6 @@ { "name": "libcachesim-node", - "version": "1.0.0", + "version": "1.0.1", "main": "index.js", "bin": { "cachesim-js": "./cli.js" diff --git a/libCacheSim-node/test.js b/libCacheSim-node/test.js index a1bf3b56..e4476448 100644 --- a/libCacheSim-node/test.js +++ b/libCacheSim-node/test.js @@ -53,7 +53,8 @@ function runTests() { assert(typeof libCacheSim.runSim === 'function', 'runSim function exists') && assert(typeof libCacheSim.runSimulation === 'function', 'runSimulation function exists') && assert(typeof libCacheSim.getSupportedAlgorithms === 'function', 'getSupportedAlgorithms function exists') && - assert(typeof libCacheSim.getSupportedTraceTypes === 'function', 'getSupportedTraceTypes function exists'); + assert(typeof libCacheSim.getSupportedTraceTypes === 'function', 'getSupportedTraceTypes function exists') && + assert(typeof libCacheSim.getVersion === 'function', 'getVersion function exists'); }); // Test 2: Check supported algorithms @@ -75,7 +76,15 @@ function runTests() { assert(traceTypes.includes('csv'), 'Includes CSV'); }); - // Test 4: Run default simulation + // Test 4: Check version function + test('Get version', () => { + const version = libCacheSim.getVersion(); + return assert(typeof version === 'string', 'Returns a string') && + assert(version.length > 0, 'Version is not empty') && + assert(/^\d+\.\d+\.\d+/.test(version), 'Version follows semantic versioning format'); + }); + + // Test 5: Run default simulation test('Run default simulation', () => { const result = libCacheSim.runSim(); return assert(typeof result === 'object', 'Returns an object') && @@ -88,7 +97,7 @@ function runTests() { assert(Math.abs(result.hitRatio + result.missRatio - 1.0) < 0.0001, 'Hit ratio + miss ratio ≈ 1.0'); }); - // Test 5: Run custom simulation with different algorithms + // Test 6: Run custom simulation with different algorithms test('Run custom simulations with different algorithms', () => { const algorithms = ['lru', 'fifo', 's3fifo']; let allPassed = true; @@ -110,7 +119,7 @@ function runTests() { return allPassed; }); - // Test 6: Test different cache sizes + // Test 7: Test different cache sizes test('Test different cache sizes', () => { const sizes = ['512kb', '1mb', '2mb']; let allPassed = true; @@ -131,7 +140,7 @@ function runTests() { return allPassed; }); - // Test 7: Error handling - invalid trace file + // Test 8: Error handling - invalid trace file test('Error handling for invalid trace file', () => { try { libCacheSim.runSimulation('/nonexistent/file.vscsi', 'vscsi', 'lru', '1mb'); @@ -141,7 +150,7 @@ function runTests() { } }); - // Test 8: Error handling - invalid algorithm + // Test 9: Error handling - invalid algorithm test('Error handling for invalid algorithm', () => { try { libCacheSim.runSimulation('../data/cloudPhysicsIO.vscsi', 'vscsi', 'invalid_algo', '1mb'); @@ -151,7 +160,7 @@ function runTests() { } }); - // Test 9: Error handling - invalid trace type + // Test 10: Error handling - invalid trace type test('Error handling for invalid trace type', () => { try { libCacheSim.runSimulation('../data/cloudPhysicsIO.vscsi', 'invalid_type', 'lru', '1mb'); @@ -161,7 +170,7 @@ function runTests() { } }); - // Test 10: Performance test - measure execution time + // Test 11: Performance test - measure execution time test('Performance measurement', () => { const startTime = process.hrtime.bigint(); const result = libCacheSim.runSim(); diff --git a/libCacheSim/include/config.h.in b/libCacheSim/include/config.h.in index 95bd564f..4e78b1f8 100644 --- a/libCacheSim/include/config.h.in +++ b/libCacheSim/include/config.h.in @@ -1,13 +1,10 @@ - /* not used */ #define VERSION_MAJOR @libCacheSim_VERSION_MAJOR @ #define VERSION_MINOR @libCacheSim_VERSION_MINOR @ #define VERSION_PATCH @libCacheSim_VERSION_PATCH @ -#define VERSION_STRING \ - "@libCacheSim_VERSION_MAJOR@.@libCacheSim_VERSION_MINOR@.@libCacheSim_" \ - "VERSION_PATCH@" +#define VERSION_STRING "@libCacheSim_VERSION_MAJOR@.@libCacheSim_VERSION_MINOR@.@libCacheSim_VERSION_PATCH@" #cmakedefine HASH_TYPE @HASH_TYPE @ diff --git a/scripts/sync_node_version.py b/scripts/sync_node_version.py new file mode 100755 index 00000000..d45a391a --- /dev/null +++ b/scripts/sync_node_version.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Script to synchronize version between libCacheSim main project and Node.js binding. + +This script reads the version from version.txt and updates the package.json +in libCacheSim-node to match. +""" + +import json +import os +import sys +from pathlib import Path + + +def get_project_root(): + """Get the project root directory.""" + script_dir = Path(__file__).parent + return script_dir.parent + + +def read_main_version(): + """Read version from version.txt.""" + project_root = get_project_root() + version_file = project_root / "version.txt" + + if not version_file.exists(): + print(f"Error: {version_file} not found", file=sys.stderr) + sys.exit(1) + + with open(version_file, 'r') as f: + version = f.read().strip() + + if not version: + print("Error: version.txt is empty", file=sys.stderr) + sys.exit(1) + + return version + + +def update_package_json(version): + """Update package.json with the new version.""" + project_root = get_project_root() + package_json_path = project_root / "libCacheSim-node" / "package.json" + + if not package_json_path.exists(): + print(f"Error: {package_json_path} not found", file=sys.stderr) + sys.exit(1) + + # Read current package.json + with open(package_json_path, 'r') as f: + package_data = json.load(f) + + current_version = package_data.get('version', 'unknown') + + if current_version == version: + print(f"Version already up to date: {version}") + return False + + # Update version + package_data['version'] = version + + # Write back to file with proper formatting + with open(package_json_path, 'w') as f: + json.dump(package_data, f, indent=2) + f.write('\n') # Add trailing newline + + print(f"Updated Node.js binding version: {current_version} → {version}") + return True + + +def main(): + """Main function.""" + try: + # Read main project version + main_version = read_main_version() + print(f"Main project version: {main_version}") + + # Update Node.js binding version + updated = update_package_json(main_version) + + if updated: + print("✓ Node.js binding version synchronized successfully") + else: + print("✓ No changes needed") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/version.txt b/version.txt new file mode 100644 index 00000000..7dea76ed --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.1