diff --git a/.gitattributes b/.gitattributes index ef828126..a444d615 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ ############################################################################### * text=auto *.sh text eol=lf +*.bat text eol=crlf ############################################################################### # Set default behavior for command prompt diff. diff --git a/.vscode/launch.docker.json b/.vscode/launch.docker.json index 6e45e980..e841cb28 100644 --- a/.vscode/launch.docker.json +++ b/.vscode/launch.docker.json @@ -93,7 +93,8 @@ }, "name": "ALPR", "type": "python", - "python": "/app/modules/ALPR/bin/linux/python38/venv/bin/python", + // "python": "/app/modules/ALPR/bin/ubuntu/python38/venv/bin/python", + "python": "/app/modules/ALPR/bin/debian/python38/venv/bin/python", "request": "launch", "program": "ALPR_adapter.py", "console": "integratedTerminal", diff --git a/.vscode/launch.json b/.vscode/launch.json index 73f3e55a..14758450 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,9 +13,12 @@ "request": "launch", "preLaunchTask": "build-server", // "postDebugTask": "stop-all", - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server", "linux": { - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server.dll", + // This is what you get from trusting ChatGPT + // "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server.dll", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server.dll", + }, "args": [], "cwd": "${workspaceFolder}/src/server/", @@ -82,9 +85,9 @@ "request": "launch", "preLaunchTask": "build-all", // "postDebugTask": "stop-all", - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server", "linux": { - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server.dll", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server.dll", }, "args": [], "cwd": "${workspaceFolder}/src/server/", @@ -138,9 +141,9 @@ "name": "Launch Server", "type": "coreclr", "request": "launch", - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server", "linux": { - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server.dll", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server.dll", }, "args": [], "cwd": "${workspaceFolder}/src/server/", @@ -188,14 +191,14 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build-server", - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server.exe", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server.exe", "linux": { - "program": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/CodeProject.AI.Server.dll", + "program": "${workspaceFolder}/src/server/bin/Debug/net9.0/CodeProject.AI.Server.dll", }, "args": [ "--ModuleOptions:LaunchModules=false" ], - "cwd": "${workspaceFolder}/src/server/bin/Debug/${env:dotNetTarget}/", + "cwd": "${workspaceFolder}/src/server/bin/Debug/net9.0/", "stopAtEntry": false, /* "serverReadyAction": { @@ -230,9 +233,9 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build-yolo-net", - "program": "${workspaceFolder}/modules/ObjectDetectionYOLOv5Net/bin/Debug/${env:dotNetTarget}/ObjectDetectionYOLOv5Net", + "program": "${workspaceFolder}/modules/ObjectDetectionYOLOv5Net/bin/Debug/net9.0/ObjectDetectionYOLOv5Net.exe", "linux": { - "program": "${workspaceFolder}/modules/ObjectDetectionYOLOv5Net/bin/Debug/${env:dotNetTarget}/ObjectDetectionYOLOv5Net.dll", + "program": "${workspaceFolder}/modules/ObjectDetectionYOLOv5Net/bin/Debug/net9.0/ObjectDetectionYOLOv5Net.dll", }, // "args": [ "--selftest" ], // "cwd": "${workspaceFolder}", @@ -256,12 +259,13 @@ }, "name": "Object Detect YOLOv5 6.2", "type": "debugpy", - "python": "${workspaceFolder}/runtimes/bin/linux/python38/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/windows/python37/venv/Scripts/python", "windows": { "python": "${workspaceFolder}/runtimes/bin/windows/python37/venv/Scripts/python" }, "linux": { - "python": "${workspaceFolder}/runtimes/bin/linux/python38/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/ubuntu/python38/venv/bin/python", + // "python": "${workspaceFolder}/runtimes/bin/debian/python38/venv/bin/python", }, "osx": { "python": "${workspaceFolder}/runtimes/bin/macos/python38/venv/bin/python", @@ -332,9 +336,9 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build-parsejson", - "program": "${workspaceFolder}/utils/ParseJSON/bin/Debug/${env:dotNetTarget}/ParseJSON", + "program": "${workspaceFolder}/utils/ParseJSON/bin/Debug/net9.0/ParseJSON", "linux": { - "program": "${workspaceFolder}/utils/ParseJSON/bin/Debug/${env:dotNetTarget}/ParseJSON.dll", + "program": "${workspaceFolder}/utils/ParseJSON/bin/Debug/net9.0/ParseJSON.dll", }, "args": [ "$.Modules.ALPR.Platforms", "test.json" ], "cwd": "${workspaceFolder}/utils/ParseJSON/", @@ -413,7 +417,7 @@ "program": "object-detect.exe", "args": [ "run" ], "cwd": "${workspaceFolder}/src/demos/clients/Rust/object-detect/target/debug", - "preLaunchTask": "build-deno-rust", + "preLaunchTask": "build-demo-rust", "stopOnEntry": false, "env": { }, @@ -451,7 +455,7 @@ "request": "launch", "preLaunchTask": "build-ai-explorer", "windows": { - "program": "${workspaceFolder}/src/demos/clients/Net/AiExplorer/bin/Debug/${env:dotNetTarget}-windows/AiExplorer.exe", + "program": "${workspaceFolder}/src/demos/clients/Net/AiExplorer/bin/Debug/net9.0-windows/AiExplorer.exe", }, "args": [], "cwd": "${workspaceFolder}", @@ -472,12 +476,9 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build-json-client", - "program": "${workspaceFolder}/src/demos/clients/Net/JsonAPI/bin/Debug/${env:dotNetTarget}/JsonAPI", + "program": "${workspaceFolder}/src/demos/clients/Net/JsonAPI/bin/Debug/net9.0/JsonAPI", "linux": { - "program": "${workspaceFolder}/src/demos/clients/Net/JsonAPI/bin/Debug/${env:dotNetTarget}/JsonAPI.dll", - }, - "osx": { - "program": "${workspaceFolder}/src/demos/clients/Net/JsonAPI/bin/Debug/${env:dotNetTarget}/JsonAPI.dll", + "program": "${workspaceFolder}/src/demos/clients/Net/JsonAPI/bin/Debug/net9.0/JsonAPI.dll", }, "args": [], "cwd": "${workspaceFolder}/src/demos/clients/Net/JsonAPI/", @@ -501,7 +502,8 @@ "python": "${workspaceFolder}/runtimes/bin/windows/python39/venv/Scripts/python.exe" }, "linux": { - "python": "${workspaceFolder}/runtimes/bin/linux/python38/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/ubuntu/python38/venv/bin/python", + // "python": "${workspaceFolder}/runtimes/bin/debian/python38/venv/bin/python", }, "osx": { "python": "${workspaceFolder}/runtimes/bin/macos/python38/venv/bin/python", @@ -528,12 +530,13 @@ "name": "Python Simple Module", "type": "debugpy", - "python": "${workspaceFolder}/runtimes/bin/linux/python39/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/windows/python39/venv/Scripts/python", "windows": { "python": "${workspaceFolder}/runtimes/bin/windows/python39/venv/Scripts/python" }, "linux": { - "python": "${workspaceFolder}/runtimes/bin/linux/python38/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/ubuntu/python38/venv/bin/python", + // "python": "${workspaceFolder}/runtimes/bin/debian/python38/venv/bin/python", }, "osx": { "python": "${workspaceFolder}/runtimes/bin/macos/python38/venv/bin/python", @@ -563,12 +566,13 @@ "name": "Python Long Process Module", "type": "debugpy", - "python": "${workspaceFolder}/runtimes/bin/linux/python39/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/windows/python39/venv/Scripts/python", "windows": { "python": "${workspaceFolder}/runtimes/bin/windows/python39/venv/Scripts/python" }, "linux": { - "python": "${workspaceFolder}/runtimes/bin/linux/python38/venv/bin/python", + "python": "${workspaceFolder}/runtimes/bin/ubuntu/python38/venv/bin/python", + // "python": "${workspaceFolder}/runtimes/bin/debian/python38/venv/bin/python", }, "osx": { "python": "${workspaceFolder}/runtimes/bin/macos/python38/venv/bin/python", @@ -596,10 +600,10 @@ "name": ".NET Simple Module", "type": "coreclr", "request": "launch", - "preLaunchTask": "build-demo-net", - "program": "${workspaceFolder}/src/demos/modules/DotNetSimple/bin/Debug/${env:dotNetTarget}/DotNetSimple", + "preLaunchTask": "demo_module_dotnetsimple", + "program": "${workspaceFolder}/src/demos/modules/DotNetSimple/bin/Debug/net9.0/DotNetSimple", "linux": { - "program": "${workspaceFolder}/src/demos/modules/DotNetSimple/bin/Debug/${env:dotNetTarget}/DotNetSimple.dll", + "program": "${workspaceFolder}/src/demos/modules/DotNetSimple/bin/Debug/net9.0/DotNetSimple.dll", }, // "args": [ "--selftest" ], // "cwd": "${workspaceFolder}", @@ -624,9 +628,9 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build-demo_module_dotnetlongprocess", - "program": "${workspaceFolder}/src/demos/modules/DotNetLongProcess/bin/Debug/${env:dotNetTarget}/DotNetLongProcess", + "program": "${workspaceFolder}/src/demos/modules/DotNetLongProcess/bin/Debug/net9.0/DotNetLongProcess", "linux": { - "program": "${workspaceFolder}/src/demos/modules/DotNetLongProcess/bin/Debug/${env:dotNetTarget}/DotNetLongProcess.dll", + "program": "${workspaceFolder}/src/demos/modules/DotNetLongProcess/bin/Debug/net9.0/DotNetLongProcess.dll", }, // "args": [ "--selftest" ], // "cwd": "${workspaceFolder}", diff --git a/.vscode/settings.json b/.vscode/settings.json index 0d894b36..ac3cf116 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,22 +10,32 @@ "appsettings", "argmax", "Armv", + "arounds", + "ASKPASS", "ASPNETCORE", "astype", "AUTOINSTALL", "aync", "BASEPATH", + "bitness", "callbacktask", "Cartooniser", "categorised", + "chcp", "codeproject", + "colour", "colourised", "colours", + "computecap", "Consolas", + "consoleloggerparameters", "CPAI", "createallsubdirs", + "Crockford", "Cuda", + "cuDNN", "CUDNN", + "Darkmode", "Denoising", "denormalize", "deserialise", @@ -33,20 +43,31 @@ "deserialising", "deskew", "Dgram", + "distutils", "downloadables", "downsampling", "dtype", "edgetpu", "endtimer", + "ensurepip", + "errorlevel", "Everytime", "Expando", "facerec", "fbytes", "fiftyone", "fileglob", + "filesize", + "findstr", "formdata", "fouo", + "generalise", "getframe", + "giga", + "gigaunit", + "gpuname", + "HKCU", + "HKLM", "hostbuilder", "hostnames", "hyps", @@ -66,17 +87,31 @@ "Jetson", "keypair", "keypoints", + "kilounit", "labelledby", "Lanczos", "licence", + "LOCALAPPDATA", + "logicaldisk", "logvals", + "lproj", "maxs", + "megas", + "megaunit", "membuffer", + "memfree", + "memsize", + "memtotal", "memused", "millidegree", + "moduleid", "modulesettings", "nbsp", + "NETBIOS", + "noheader", + "norestart", "normalises", + "noscroll", "nowait", "nvcc", "objectdetection", @@ -87,54 +122,86 @@ "orangepi", "otsu", "paddleocr", + "parsejson", "peasy", + "physmem", "platenumber", "pluralise", + "PMIC", + "popd", "postinst", "postprocess", "prerm", + "pstree", + "pushd", "pycoral", + "pypi", "pyproj", + "pythonhosted", "pywrap", "Quadro", "QUEUENAME", + "Radxa", + "radxarock", "raspberrypi", "rebalancing", "RECOG", "recognised", "recursesubdirs", + "redist", + "redistributables", + "regpath", + "rembg", "reqid", "reqtype", "reso", "RKNN", + "Rockchip", "rocm", + "rocminfo", "runhidden", "runtimes", "safetensors", "selftest", "serversettings", + "setuptools", + "setx", "shellexec", + "sideloaded", "skia", + "Sonoma", + "standardise", "starttimer", "statuseseses", + "subshell", "Systemmd", "Tegra", "tegrastats", "tempstore", + "tera", + "teras", + "teraunit", "textreader", "tflite", "topbanner", "torchvision", + "totalspacebytes", "tpus", "ufeff", "unclip", "uninstallexe", "upsampling", + "usebackq", "userid", + "usermem", + "utilisation", "utilise", + "vcgencmd", "venv", "vggish", + "waitretry", "wavfile", + "winget", "womp", "wslconfig", "wwwroot", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 38f5c1f5..fc884fc6 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -26,6 +26,19 @@ } }, + { + "label": "build-modules", + "group": "build", + "dependsOrder": "sequence", + "dependsOn": [ + "build-yolo-net", + "build-portraitfilter", + "build-sentimentanalysis", + "build-demo_module_dotnetlongprocess", + "demo_module_dotnetsimple" + ] + }, + // Build individual projects =============================================================== { @@ -96,7 +109,8 @@ "build", "${workspaceFolder}/modules/ObjectDetectionYOLOv5Net", "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" + "/consoleloggerparameters:NoSummary", + "--configuration", "Debug" ], "problemMatcher": "$msCompile" }, @@ -116,7 +130,7 @@ }, { - "label": "build-demo-net", // Builds ONLY the demo .NET analysis module + "label": "demo_module_dotnetsimple", // Builds ONLY the demo .NET analysis module "type": "process", "group": "build", "command": "dotnet", @@ -154,7 +168,7 @@ }, { - "label": "build-deno-rust", + "label": "build-demo-rust", "type": "shell", "command": "cd \"${workspaceFolder}\\src\\demos\\clients\\Rust\\object-detect\" & cargo build", "group": { @@ -166,7 +180,7 @@ }, { - "label": "run-deno-rust", + "label": "run-demo-rust", "type": "shell", "command": "cd \"${workspaceFolder}\\src\\demos\\clients\\Rust\\object-detect\" & cargo run", "group": { @@ -184,7 +198,7 @@ "command": "dotnet", "args": [ "build", - "${workspaceFolder}/../CodeProject.AI-Modules/PortraitFilter", + "${workspaceFolder}/../CodeProject.AI-Modules/CodeProject.AI-PortraitFilter", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -198,7 +212,7 @@ "command": "dotnet", "args": [ "build", - "${workspaceFolder}/../CodeProject.AI-Modules/SentimentAnalysis", + "${workspaceFolder}/../CodeProject.AI-Modules/CodeProject.AI-SentimentAnalysis", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], diff --git a/CodeProject.AI-Server.code-workspace b/CodeProject.AI-Server.code-workspace index 78872ccf..c93b98d9 100644 --- a/CodeProject.AI-Server.code-workspace +++ b/CodeProject.AI-Server.code-workspace @@ -86,6 +86,9 @@ } ], "settings": { - "csharp.debug.logging.moduleLoad": false + "csharp.debug.logging.moduleLoad": false, + "cSpell.words": [ + "paddlepaddle" + ] } } \ No newline at end of file diff --git a/README.md b/README.md index 334848ca..8fcba501 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ We will be constantly expanding the feature list. #### Supported Development Environments -This current release works best with Visual Studio Code on Windows 10+. Ubuntu, Debian and macOS (both Intel and Apple Silicon). Visual Studio 2019+ support is included for Windows 10+. +This current release works best with Visual Studio Code on Windows 10+. Ubuntu 22.04+, Debian and macOS (both Intel and Apple Silicon). Visual Studio 2019+ support is included for Windows 10+. The current release provides support for CPU on each platform, DirectML on Windows, CUDA on Windows and Linux, support for Apple Silicon GPUs, RockChip NPUs and Coral.AI TPUs. Support depends on the module itself. @@ -129,6 +129,19 @@ The current release provides support for CPU on each platform, DirectML on Windo - [Installing CodeProject.AI on your machine](https://codeproject.github.io/codeproject.ai/why/install_on_windows.html). For those who have CodeProject.AI integrated with Home Assist or Blue Iris - [Setting up the development environment](https://codeproject.github.io/codeproject.ai/devguide/install_dev.html) (spoiler: it's easy!) - - [Running in Docker](https://codeproject.github.io/codeproject.ai/why/running_in_docker.html) - - Setup or install issues? See [Common Errors](https://codeproject.github.io/codeproject.ai/devguide/common_errors.html) + - [Running in Docker](https://codeproject.github.io/codeproject.ai/install/running_in_docker.html) + - Setup or install issues? See the [FAQs](https://codeproject.github.io/codeproject.ai/faq/index.html) + +I'll add this to the docs: + +## Latest Version changes: 2.9 + +- Updated to .NET 9 +- Support for Ubuntu 24.10 +- Improved CUDA 12 support +- Improvements to CUDA support in Windows and Linux +- Further Windows arm64 fixes +- Further macOS arm64 fixes +- General dev environment setup fixes +- Fixes for Windows installer when wget is missing diff --git a/devops/install/clean.bat b/devops/install/clean.bat index 58583410..48a77ef2 100644 --- a/devops/install/clean.bat +++ b/devops/install/clean.bat @@ -9,6 +9,8 @@ cls setlocal enabledelayedexpansion +start ..\utils\stop_all.bat + set useColor=true set doDebug=false set lineWidth=70 @@ -111,6 +113,17 @@ if /i "!cleanInstallAll!" == "true" ( set cleanUserData=true ) +echo clean assets = %cleanAssets% +echo clean build = %cleanBuild% +echo clean data = %cleanUserData% +echo clean download-cache = %cleanDownloadCache% +echo clean install = %cleanInstallCurrentOS% +echo clean install-all = %cleanInstallAll% +echo clean libraries = %cleanLibraries% +echo clean libraries-all = %cleanLibrariesAll% +echo clean all = %cleanAll% + + REM Start cleaning ============================================================= if /i "%cleanAssets%" == "true" ( @@ -210,11 +223,9 @@ if /i "%cleanDownloadCache%" == "true" ( call "!utilsScript!" WriteLine REM remove non module or model folders - FOR /d %%a IN ("%rootDir%\downloads\*") DO ( + FOR /d %%a IN ("%rootDir%\downloads\modules\assets\*") DO ( IF /i NOT "%%~nxa"=="modules" IF /i NOT "%%~nxa"=="models" call :RemoveDir "%%a" - - REM clean out module folders - call :RemoveFile "%rootDir%\downloads\modules\" + ) REM clean out models files FOR %%a IN ("%rootDir%\downloads\models\*") DO ( @@ -246,7 +257,7 @@ if /i "%cleanInstallCurrentOS%" == "true" ( if /i "%cleanInstallAll%" == "true" ( call "!utilsScript!" WriteLine - call "!utilsScript!" WriteLine "Cleaning install for other platforms" "White" "Blue" !lineWidth! + call "!utilsScript!" WriteLine "Cleaning install for all platforms" "White" "Blue" !lineWidth! call "!utilsScript!" WriteLine REM Clean shared python installs and venvs diff --git a/devops/install/clean.sh b/devops/install/clean.sh index 673a3c55..08c7dfdc 100644 --- a/devops/install/clean.sh +++ b/devops/install/clean.sh @@ -11,6 +11,9 @@ useColor=true doDebug=false lineWidth=70 + bash ../utils/stop_all.sh + + # List of modules we'll look after =======================++==================== dotNetModules=( "ObjectDetectionYOLOv5Net" ) @@ -369,11 +372,6 @@ if [ "$cleanBuild" = true ]; then rm "${rootDir}/src/demos/modules/${dirName}/${dirName}-*" done - cleanSubDirs "${rootDir}/Installers/Windows" "bin/Debug/" - cleanSubDirs "${rootDir}/Installers/Windows" "bin/Release/" - cleanSubDirs "${rootDir}/Installers/Windows" "obj/Debug/" - cleanSubDirs "${rootDir}/Installers/Windows" "obj/Release/" - removeDir "${rootDir}/utils/ParseJSON/bin" removeDir "${rootDir}/utils/ParseJSON/obj" rm "${rootDir}/utils/ParseJSON/ParseJSON.deps.json" @@ -424,20 +422,20 @@ if [ "$cleanInstallCurrentOS" = true ]; then writeLine # Clean shared python venvs - removeDir "${rootDir}/runtimes/bin/${os}/" + removeDir "${rootDir}/runtimes/bin/${platform_dir}/" # Clean module python venvs for dirName in "${pythonModules[@]}" do - removeDir "${rootDir}/modules/${dirName}/bin/${os}/" + removeDir "${rootDir}/modules/${dirName}/bin/${platform_dir}/" done for dirName in "${pythonExternalModules[@]}" do - removeDir "${externalModulesDir}/${dirName}/bin/${os}/" + removeDir "${externalModulesDir}/${dirName}/bin/${platform_dir}/" done for dirName in "${pythonDemoModules[@]}" do - removeDir "${rootDir}/src/demos/modules/${dirName}/bin/${os}/" + removeDir "${rootDir}/src/demos/modules/${dirName}/bin/${platform_dir}/" done fi @@ -472,20 +470,20 @@ if [ "$cleanLibraries" = true ]; then writeLine # Clean shared python venvs - delPattern "${rootDir}/runtimes/bin/${os}/python*/venv/lib/python*/site-packages/*" + delPattern "${rootDir}/runtimes/bin/${platform_dir}/python*/venv/lib/python*/site-packages/*" # Clean module python venvs for dirName in "${pythonModules[@]}" do - delPattern "${rootDir}/modules/${dirName}/bin/${os}/python*/venv/lib/python*/site-packages/*" + delPattern "${rootDir}/modules/${dirName}/bin/${platform_dir}/python*/venv/lib/python*/site-packages/*" done for dirName in "${pythonExternalModules[@]}" do - delPattern "${externalModulesDir}/${dirName}/bin/${os}/python*/venv/lib/python*/site-packages/*" + delPattern "${externalModulesDir}/${dirName}/bin/${platform_dir}/python*/venv/lib/python*/site-packages/*" done for dirName in "${pythonDemoModules[@]}" do - delPattern "${rootDir}/src/demos/modules/${dirName}/bin/${os}/python*/venv/lib/python*/site-packages/*" + delPattern "${rootDir}/src/demos/modules/${dirName}/bin/${platform_dir}/python*/venv/lib/python*/site-packages/*" done fi diff --git a/devops/install/dotnet-install-arm.sh b/devops/install/dotnet-install-arm.sh index 881ecf2f..5edf8570 100644 --- a/devops/install/dotnet-install-arm.sh +++ b/devops/install/dotnet-install-arm.sh @@ -5,7 +5,7 @@ bold="\e[1m" underline="\e[1;4m" reset="\e[0m" -echo -e "${bold}.NET 7 Installer${reset}" +echo -e "${bold}.NET 9 Installer${reset}" echo -e "${bold}Pete Codes / PJG Creations 2021${reset}" echo -e "${bold}CodeProject.AI edition${reset}" # Drastically trimming output / SDK is optional @@ -135,7 +135,7 @@ fi echo -e "${bold}Creating Main Directory${reset}" if [[ -d /opt/dotnet ]]; then - echo "/opt/dotnet already exists on your filesystem." + echo "/opt/dotnet already exists on your filesystem." else echo "Creating Main Directory" mkdir /opt/dotnet diff --git a/devops/install/dotnet-install.sh b/devops/install/dotnet-install.sh index 08acd9e3..a6de81f2 100644 --- a/devops/install/dotnet-install.sh +++ b/devops/install/dotnet-install.sh @@ -329,6 +329,14 @@ get_machine_architecture() { echo "loongarch64" return 0 ;; + riscv64) + echo "riscv64" + return 0 + ;; + powerpc|ppc) + echo "ppc" + return 0 + ;; esac fi @@ -417,11 +425,17 @@ get_normalized_architecture_for_specific_sdk_version() { # args: # version or channel - $1 is_arm64_supported() { - #any channel or version that starts with the specified versions - case "$1" in - ( "1"* | "2"* | "3"* | "4"* | "5"*) - echo false - return 0 + # Extract the major version by splitting on the dot + major_version="${1%%.*}" + + # Check if the major version is a valid number and less than 6 + case "$major_version" in + [0-9]*) + if [ "$major_version" -lt 6 ]; then + echo false + return 0 + fi + ;; esac echo true @@ -571,12 +585,10 @@ is_dotnet_package_installed() { local dotnet_package_path="$(combine_paths "$(combine_paths "$install_root" "$relative_path_to_package")" "$specific_version")" say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path" - if [ ! -d "$dotnet_package_path" ]; then - # say_err "$dotnet_package_path does not exist" - return 1 - else - # say_err "$dotnet_package_path exists" + if [ -d "$dotnet_package_path" ]; then return 0 + else + return 1 fi } @@ -926,7 +938,15 @@ get_user_install_path() { if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then echo "$DOTNET_INSTALL_DIR" else - echo "$HOME/.dotnet" + if [ "$normalized_os" == "osx" ]; then + # if [ "$(get_machine_architecture)" = "arm64" ]; then + # echo "/opt/dotnet" + # else + echo "/usr/local/share/dotnet" + # fi + else + echo "$HOME/.dotnet" + fi fi return 0 } @@ -958,6 +978,42 @@ get_absolute_path() { return 0 } +# args: +# override - $1 (boolean, true or false) +get_cp_options() { + eval $invocation + + local override="$1" + local override_switch="" + + if [ "$override" = false ]; then + # cp: warning: behavior of -n is non-portable and may change in future; use --update=none instead + if [ "$normalized_os" == "osx" ]; then + override_switch="-n" + else + override_switch="--update=none" + fi; + + # create temporary files to check if 'cp -u' is supported + tmp_dir="$(mktemp -d)" + tmp_file="$tmp_dir/testfile" + tmp_file2="$tmp_dir/testfile2" + + touch "$tmp_file" + + # use -u instead of -n if it's available + if cp -u "$tmp_file" "$tmp_file2" 2>/dev/null; then + override_switch="-u" + fi + + # clean up + rm -f "$tmp_file" "$tmp_file2" + rm -rf "$tmp_dir" + fi + + echo "$override_switch" +} + # args: # input_files - stdin # root_path - $1 @@ -969,15 +1025,7 @@ copy_files_or_dirs_from_list() { local root_path="$(remove_trailing_slash "$1")" local out_path="$(remove_trailing_slash "$2")" local override="$3" - local osname="$(get_current_os_name)" - local override_switch=$( - if [ "$override" = false ]; then - if [ "$osname" = "linux-musl" ]; then - printf -- "-u"; - else - printf -- "-n"; - fi - fi) + local override_switch="$(get_cp_options "$override")" cat | uniq | while read -r file_path; do local path="$(remove_beginning_slash "${file_path#$root_path}")" @@ -1030,8 +1078,7 @@ extract_dotnet_package() { say "extract_dotnet_package: zip_path=$zip_path, temp_out_path=$temp_out_path, out_path=$out_path" - if [ ! -d "$temp_out_path" ]; then mkdir "$temp_out_path"; fi - + local failed=false if [ "$quiet" != true ]; then tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true @@ -1045,7 +1092,7 @@ extract_dotnet_package() { validate_remote_local_file_sizes "$zip_path" "$remote_file_size" - # rm -rf "$temp_out_path" + rm -rf "$temp_out_path" if [ -z ${keep_zip+x} ]; then rm -f "$zip_path" && say_verbose "Temporary archive file $zip_path was removed" fi @@ -1761,7 +1808,7 @@ do zip_path="$1" ;; -?|--?|-h|--help|-[Hh]elp) - script_name="$(basename "$0")" + script_name="dotnet-install.sh" echo ".NET Tools Installer" echo "Usage:" echo " # Install a .NET SDK of a given Quality from a given Channel" @@ -1892,4 +1939,4 @@ fi say "Note that the script does not resolve dependencies during installation." say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section." -say "Installation finished successfully." \ No newline at end of file +say "Installation finished successfully." diff --git a/devops/install/install_CUDnn.bat b/devops/install/install_CUDnn.bat deleted file mode 100644 index b2d64e31..00000000 --- a/devops/install/install_CUDnn.bat +++ /dev/null @@ -1,283 +0,0 @@ -:: CodeProject.AI Server -:: -:: Windows cuDNN install script -:: -:: BEFORE YOU START: Make sure you have -:: -:: a) CUDA 11.7 drivers (https://www.nvidia.com/Download/index.aspx) installed, and -:: b) CUDA Toolkit 11.7 (https://developer.nvidia.com/cuda-downloads) installed. -:: -:: What this script does: -:: -:: 1. Downloads the cuDNN package (v8.9.4.96 for CUDA 11) -:: -:: 2. Creates a folder "C:\Program Files\NVIDIA\CUDNN\v8.9" and extracts the cuDNN package -:: into that folder. There will be bin, lib and include folders, plus a LICENSE file. -:: -:: 3. Adds this path to the PATH environment variable: -:: setx /M PATH = Path + "%PATH%;C:\Program Files\NVIDIA\CUDNN\v8.9\bin" -:: -:: 4. Downloads ZLib from WinImage (http://www.winimage.com/zLibDll/zlib123dllx64.zip) and extracts -:: into a folder. Since it's being used by cuDNN it's easier to just extract into the -:: cuDNN folder: "C:\Program Files\NVIDIA\CUDNN\v8.9\zlib -:: -:: 5. Add this path to the PATH environment variable: -:: setx /M PATH "%PATH%;C:\Program Files\NVIDIA\CUDNN\v8.9\zlib\dll_x64" -:: -:: What you need to do: just double click this bat file in Windows - -@echo off -cls -setlocal enabledelayedexpansion - -REM Force admin mode -cd /D "%~dp0" -if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b) - - -REM Settings - -set dryRun=false - -set cuDNNLocation=https://developer.nvidia.com/rdp/cudnn-download -set cuDNNArchiveDownloadUrl=https://codeproject-ai.s3.ca-central-1.amazonaws.com/server/assets/libraries/ - -set CUDA10_cuDNN_version=8.4.1.50 -set CUDA11_cuDNN_version=8.9.4.25 -set CUDA12_cuDNN_version=8.9.7.29 - -set zLibLocation=http://www.winimage.com/zLibDll/ -set zLibArchiveName=zlib123dllx64 - - -REM Installer archive - -call :GetCudaVersion - -if "!cuda_major_version!" == "" ( - if /i "!dryRun!" == "true" ( - set cuda_major_version=12 - ) else ( - echo CUDA was not found. Exiting. - goto:eof - ) -) -echo Found CUDA !cuda_major_version! - -REM Get the name of the appropriate installer based on the major CUDA version. We -REM use this in the general case where no installer is present in the local folder -REM The cuDNN installer file has a name like cudnn-windows-x86_64-8.5.0.96_cuda11-archive.zip -REM Specific versions we have archived: -REM cudnn-windows-x86_64-8.4.1.50_cuda10.2-archive.zip -REM cudnn-windows-x86_64-8.9.4.25_cuda11-archive.zip -REM cudnn-windows-x86_64-8.9.7.29_cuda12-archive.zip -REM Before downloading we will search the local folder to see if there is already -REM an installer present. - -if "!cuda_major_version!" == "10" ( - set cuda_version=10.2 - set cuDNN_version=!CUDA10_cuDNN_version! -) else if "!cuda_major_version!" == "11" ( - set cuda_version=11 - set cuDNN_version=!CUDA11_cuDNN_version! -) else if "!cuda_major_version!" == "12" ( - set cuda_version=12 - set cuDNN_version=!CUDA12_cuDNN_version! -) -set cuDNNArchiveFilename=cudnn-windows-x86_64-!cuDNN_version!_cuda!cuda_version!-archive.zip -set cuDNNPattern=cudnn-windows-x86_64-*_cuda!cuda_version!-archive.zip -set "cuDNNRegex=cudnn-windows-x86_64-([0-9]*).([0-9]*).([0-9]*).([0-9]*)_cuda!cuda_version!-archive.zip" - - -REM Install - -echo ======================================================================== -echo. -echo Setting up cuDNN for CodeProject.AI Server -echo. -echo ======================================================================== -echo. - -set cuDNNInstalled=false -set zLibInstalled=false - -:: Walk through the modules directory to find the cuDNN setup file, starting newest to oldest, -:: but before we start lets ensure we attempt to have at least one version present. - -echo Searching for existing cuDNN installers !cuDNNPattern! -IF not exist "!cuDNNPattern!" ( - echo No cuDNN archive found. Downloading !cuDNNArchiveFilename!... - powershell -command "Start-BitsTransfer -Source '!cuDNNArchiveDownloadUrl!!cuDNNArchiveFilename!' -Destination '!cuDNNArchiveFilename!'" -) - -IF exist "!cuDNNPattern!" ( - for /F "usebackq delims=" %%f in (`dir /B /A-D /O-N !cuDNNPattern!`) do ( - - REM We have a cuDNN archive. Get the archive name with and without extension - set cuDNNInstallerFilename=%%~nxf - set cuDNNInstallerNameNoExt=%%~nf - - echo Found !cuDNNInstallerFilename! - - REM Get the version. Filename is similar to cudnn-windows-x86_64-8.5.0.96_cuda11-archive.zip, - REM where the version here is 8.5.0.96. We only need major/minor. - - for /f "delims=" %%i in (' - powershell -c "'!cuDNNInstallerFilename!' -replace '!cuDNNRegex!','$1.$2'" - ') do set version=%%i - - if "!version!" == "" ( - echo No installer available. - goto:eof - ) - echo Found cuDNN installer for version !version!. Expanding... - - REM Expand the archive - - rem echo Expanding... - - set tarExists=true - tar -xf "!cuDNNInstallerFilename!" > nul 2>nul - if "%errorlevel%" == "9009" set tarExists=false - - if "!tarExists!" == "false" ( - powershell -command "Expand-Archive -Path '!cuDNNInstallerFilename!' -DestinationPath '!cuDNNInstallerNameNoExt!' -Force" - ) - - REM Move the directories into C:\Program Files\NVIDIA\CUDNN\v - - echo Installing cuDNN files... - - if not exist "C:\Program Files\NVIDIA" mkdir "C:\Program Files\NVIDIA" > NUL - if not exist "C:\Program Files\NVIDIA\CUDNN" mkdir "C:\Program Files\NVIDIA\CUDNN" > NUL - if not exist "C:\Program Files\NVIDIA\CUDNN\v!version!\" mkdir "C:\Program Files\NVIDIA\CUDNN\v!version!\" > NUL - - robocopy /e "!cuDNNInstallerNameNoExt! " "C:\Program Files\NVIDIA\CUDNN\v!version! " /MOVE /NC /NS /NJS /NJH > NUL - - set cuDNNInstalled=true - - - echo Installing ZLib - - REM Next step is to grab ZLib and intall that. We'll place it next to the cuDNN files in - REM C:\Program Files\NVIDIA\CUDNN\v\zLib - - if not exist "!zLibArchiveName!.zip" ( - powershell -command "Start-BitsTransfer -Source '!zLibLocation!!zLibArchiveName!.zip' -Destination '!zLibArchiveName!.zip'" - ) - - if exist "!zLibArchiveName!.zip" ( - echo Expanding ZLib... - if "!tarExists!" == "true" ( - if not exist "!zLibArchiveName!" mkdir "!zLibArchiveName!" > NUL - copy !zLibArchiveName!.zip !zLibArchiveName!\ > NUL - pushd !zLibArchiveName! > NUL - tar -xf "!zLibArchiveName!.zip" > nul 2>nul - del !zLibArchiveName!.zip > NUL - popd > NUL - ) else ( - powershell -command "Expand-Archive -Path '!zLibArchiveName!.zip' -DestinationPath '!zLibArchiveName!' -Force" - ) - - echo Installing ZLib... - if not exist "C:\Program Files\NVIDIA\CUDNN\v!version!\zlib" mkdir "C:\Program Files\NVIDIA\CUDNN\v!version!\zlib" - robocopy /e "!zLibArchiveName! " "C:\Program Files\NVIDIA\CUDNN\v!version!\zlib\ " /MOVE /NC /NS /NJS /NJH > NUL - - set zLibInstalled=true - ) - - if /i "!dryRun!" == "false" ( - REM We need to set the PATH variable. Some caveats: - REM 1. When you set the PATH variable, %PATH% will not reflect the update you just made, so - REM doing "PATH = PATH + change1" followed by "PATH = PATH + change2" results in just Change2 - REM being added. So: do all the changes in one fell swoop. - REM 2. We can't use setx /M PATH "%PATH%;C:\Program Files\NVIDIA\CUDNN\v!version!\zlib\dll_x64" - REM because setx truncates the path to 1024 characters. In 2022. Insanity. - REM 3. Only update the path if we need to. Check for existance before modifying! - - echo Updating PATH environment variable... - - set newPath=!PATH! - - REM Add ZLib path if it hasn't already been added - if "!zLibInstalled!" == "true" ( - if /i "!PATH:C:\Program Files\NVIDIA\CUDNN\v!version!\zlib=!" == "!PATH!" ( - set newPath=!newPath!;C:\Program Files\NVIDIA\CUDNN\v!version!\zlib\dll_x64 - ) - ) - REM Add cuDNN path if it hasn't already been added - if /i "!PATH:C:\Program Files\NVIDIA\CUDNN\v!version!\bin=!" == "!PATH!" ( - set newPath=!newPath!;C:\Program Files\NVIDIA\CUDNN\v!version!\bin - ) - - if /i "!newPath" NEQ "!PATH!" ( - rem echo New Path is !newPath! - powershell -command "[Environment]::SetEnvironmentVariable('PATH', '!newPath!','Machine'); - ) - ) - - echo done. - - REM Only process the first archive we find - call :InstallChecks - ) -) - - -goto:eof - -:InstallChecks - - if /i "!cuDNNInstalled!" == "false" ( - echo No cuDNN archive found. - echo Please download CUDA 11 from !cuDNNLocation! - pause - goto:eof - ) else if /i "!zLibInstalled!" == "false" ( - echo No ZLib found. - echo Please download ZLib from !zLibLocation!!zLibArchiveName!.zip - pause - goto:eof - ) - - exit /b - -:GetCudaVersion - - rem setlocal enabledelayedexpansion - - :: Use nvcc to find the CUDA version - where nvcc >nul 2>&1 - if !errorlevel! == 0 ( - REM Get the line containing "release x.y" - for /f "tokens=*" %%i in ('nvcc --version ^| findstr /C:"release"') do set cudaLine=%%i - REM Get the 5th token in the line when split by , and spaces - for /f "tokens=5 delims=, " %%a in ("!cudaLine!") do set cuda_version=%%a - ) else ( - REM Backup attempt: Use nvidia-smi to find the CUDA version - where nvidia-smi >nul 2>&1 - if !errorlevel! == 0 ( - REM Get the line containing "CUDA Version x.y" - for /f "tokens=*" %%i in ('nvidia-smi ^| findstr /C:"CUDA Version"') do set cudaLine=%%i - REM Get the 9th token in the line when split by spaces - for /f "tokens=9 delims= " %%a in ("!cudaLine!") do set cuda_version=%%a - ) else ( - REM echo Unable to find nvcc or nvidia-smi - ) - ) - - REM echo cudaLine = !cudaLine! - REM echo GetCudaVersion version: !cuda_version! - - if "!cuda_version!" neq "" ( - for /f "tokens=1,2 delims=." %%a in ("!cuda_version!") do ( - set "cuda_major_version=%%a" - exit /b - ) - ) - - REM pass back values as in params 1 and 2 - REM set "%~1=!cuda_version!" - REM set "%~2=!cuda_major_version!" - - exit /b diff --git a/devops/install/install_cuDNN.bat b/devops/install/install_cuDNN.bat new file mode 100644 index 00000000..60e091c3 --- /dev/null +++ b/devops/install/install_cuDNN.bat @@ -0,0 +1,342 @@ +:: CodeProject.AI Server +:: +:: Windows cuDNN install script +:: +:: BEFORE YOU START: Make sure you have +:: +:: a) CUDA 11.7 drivers (https://www.nvidia.com/Download/index.aspx) installed, and +:: b) CUDA Toolkit 11.7 (https://developer.nvidia.com/cuda-downloads) installed. +:: +:: What this script does: +:: +:: 1. Downloads the cuDNN package (v8.9.4.96 for CUDA 11) +:: +:: 2. Creates a folder "C:\Program Files\NVIDIA\CUDNN\v8.9" and extracts the cuDNN package +:: into that folder. There will be bin, lib and include folders, plus a LICENSE file. +:: +:: 3. Adds this path to the PATH environment variable: +:: setx /M PATH = Path + "%PATH%;C:\Program Files\NVIDIA\CUDNN\v8.9\bin" +:: +:: 4. Downloads ZLib from WinImage (http://www.winimage.com/zLibDll/zlib123dllx64.zip) and extracts +:: into a folder. Since it's being used by cuDNN it's easier to just extract into the +:: cuDNN folder: "C:\Program Files\NVIDIA\CUDNN\v8.9\zlib +:: +:: 5. Add this path to the PATH environment variable: +:: setx /M PATH "%PATH%;C:\Program Files\NVIDIA\CUDNN\v8.9\zlib\dll_x64" +:: +:: What you need to do: just double click this bat file in Windows + +@echo off +cls +setlocal enabledelayedexpansion + +REM Force admin mode +cd /D "%~dp0" +if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b) + + +REM Settings + +set dryRun=false + +:: The location of large packages that need to be downloaded (eg an AWS S3 bucket +:: name). This will be overwritten using the value from appsettings.json +REM set assetStorageUrl=https://codeproject-ai.s3.ca-central-1.amazonaws.com/server/assets/ +set assetStorageUrl=https://codeproject-ai-bunny.b-cdn.net/server/assets/ +REM set assetStorageUrl=https://www.codeproject.com/ai/download/server/assets/ + +set cuDNNLocation=https://developer.nvidia.com/rdp/cudnn-download +set cuDNNArchiveDownloadUrl=!assetStorageUrl!libraries/ + +set CUDA10_cuDNN_version=8.7.0.84 +set CUDA11_cuDNN_version=8.9.7.29 +set CUDA12_cuDNN_version=9.5.1 + +set zLibLocation=http://www.winimage.com/zLibDll/ +set zLibArchiveName=zlib123dllx64 + + +REM Installer archive + +call :GetCudaVersion + +if "!cuda_major_version!" == "" ( + if /i "!dryRun!" == "true" ( + set cuda_major_version=12 + ) else ( + echo CUDA was not found. Exiting. + goto:eof + ) +) +echo Found CUDA !cuda_major_version! + +REM Get the name of the appropriate installer based on the major CUDA version. We +REM use this in the general case where no installer is present in the local folder +REM The cuDNN installer file has a name like cudnn-windows-x86_64-8.5.0.96_cuda11-archive.zip +REM Specific versions we have archived: +REM cudnn-windows-x86_64-8.4.1.50_cuda10.2-archive.zip +REM cudnn-windows-x86_64-8.9.4.25_cuda11-archive.zip +REM cudnn-windows-x86_64-8.9.7.29_cuda12-archive.zip +REM Before downloading we will search the local folder to see if there is already +REM an installer present. + +if "!cuda_major_version!" == "10" ( + set cuda_version=10.2 + set cuDNN_version=!CUDA10_cuDNN_version! +) else if "!cuda_major_version!" == "11" ( + set cuda_version=11 + set cuDNN_version=!CUDA11_cuDNN_version! +) else if "!cuda_major_version!" == "12" ( + set cuda_version=12 + set cuDNN_version=!CUDA12_cuDNN_version! +) + +if "!cuda_major_version!" == "12" ( + set cuDNNInstallerFilename=cudnn_!cuDNN_version!_windows.exe + set cuDNNPattern=cudnn_*_windows.exe + set "cuDNNRegex=cudnn-([0-9]*).([0-9]*).([0-9]*)_windows.exe" +) else ( + set cuDNNArchiveFilename=cudnn-windows-x86_64-!cuDNN_version!_cuda!cuda_version!-archive.zip + set cuDNNPattern=cudnn-windows-x86_64-*_cuda!cuda_version!-archive.zip + set "cuDNNRegex=cudnn-windows-x86_64-([0-9]*).([0-9]*).([0-9]*).([0-9]*)_cuda!cuda_version!-archive.zip" +) + +REM Check + +if exist "C:\Program Files\NVIDIA\CUDNN\v*.*" ( + echo cuDNN is installed. + goto:eof +) + + +REM Install + +echo ======================================================================== +echo. +echo Setting up cuDNN for CodeProject.AI Server +echo. +echo ======================================================================== +echo. + +set cuDNNInstalled=false +set zLibInstalled=false + +:: Walk through the modules directory to find the cuDNN setup file, starting newest to oldest, +:: but before we start lets ensure we attempt to have at least one version present. + +echo Searching for existing cuDNN installers !cuDNNPattern! +If not exist "!cuDNNPattern!" ( + echo No cuDNN archive found. Downloading !cuDNNArchiveFilename!... + if "!cuda_major_version!" == "12" ( + powershell -command "Start-BitsTransfer -Source '!cuDNNArchiveDownloadUrl!!cuDNNInstallerFilename!' -Destination '!cuDNNInstallerFilename!'" + ) else ( + powershell -command "Start-BitsTransfer -Source '!cuDNNArchiveDownloadUrl!!cuDNNArchiveFilename!' -Destination '!cuDNNArchiveFilename!'" + ) +) + +if "!cuda_major_version!" == "12" ( + If exist "!cuDNNPattern!" ( + for /F "usebackq delims=" %%f in (`dir /B /A-D /O-N !cuDNNPattern!`) do ( + + REM We have a cuDNN archive. Get the archive name with and without extension + set cuDNNInstallerFilename=%%~nxf + echo Found !cuDNNInstallerFilename! + + REM Get the version. Filename is similar to cudnn-9.5.1_windows.exe + REM where the version here is 9.5.1. We only need major/minor. + + for /f "delims=" %%i in (' + powershell -c "'!cuDNNInstallerFilename!' -replace '!cuDNNRegex!','$1.$2'" + ') do set version=%%i + + if "!version!" == "" ( + echo No installer available. + goto:eof + ) + echo Found cuDNN installer for version !version!. Installing... + + !cuDNNInstallerFilename! + + del !cuDNNInstallerFilename! + + set cuDNNInstalled=true + + echo done. + + REM Only process the first archive we find + call :InstallChecks + ) + ) + +) else ( + IF exist "!cuDNNPattern!" ( + for /F "usebackq delims=" %%f in (`dir /B /A-D /O-N !cuDNNPattern!`) do ( + + REM We have a cuDNN archive. Get the archive name with and without extension + set cuDNNInstallerFilename=%%~nxf + set cuDNNInstallerNameNoExt=%%~nf + + echo Found !cuDNNInstallerFilename! + + REM Get the version. Filename is similar to cudnn-windows-x86_64-8.5.0.96_cuda11-archive.zip, + REM where the version here is 8.5.0.96. We only need major/minor. + + for /f "delims=" %%i in (' + powershell -c "'!cuDNNInstallerFilename!' -replace '!cuDNNRegex!','$1.$2'" + ') do set version=%%i + + if "!version!" == "" ( + echo No installer available. + goto:eof + ) + echo Found cuDNN installer for version !version!. Expanding... + + REM Expand the archive + + rem echo Expanding... + + set tarExists=true + tar -xf "!cuDNNInstallerFilename!" > nul 2>nul + if "%errorlevel%" == "9009" set tarExists=false + + if "!tarExists!" == "false" ( + powershell -command "Expand-Archive -Path '!cuDNNInstallerFilename!' -DestinationPath '!cuDNNInstallerNameNoExt!' -Force" + ) + + REM Move the directories into C:\Program Files\NVIDIA\CUDNN\v + + echo Installing cuDNN files... + + if not exist "C:\Program Files\NVIDIA" mkdir "C:\Program Files\NVIDIA" > NUL + if not exist "C:\Program Files\NVIDIA\CUDNN" mkdir "C:\Program Files\NVIDIA\CUDNN" > NUL + if not exist "C:\Program Files\NVIDIA\CUDNN\v!version!\" mkdir "C:\Program Files\NVIDIA\CUDNN\v!version!\" > NUL + + robocopy /e "!cuDNNInstallerNameNoExt! " "C:\Program Files\NVIDIA\CUDNN\v!version! " /MOVE /NC /NS /NJS /NJH > NUL + + set cuDNNInstalled=true + + + echo Installing ZLib + + REM Next step is to grab ZLib and install that. We'll place it next to the cuDNN files in + REM C:\Program Files\NVIDIA\CUDNN\v\zLib + + if not exist "!zLibArchiveName!.zip" ( + powershell -command "Start-BitsTransfer -Source '!zLibLocation!!zLibArchiveName!.zip' -Destination '!zLibArchiveName!.zip'" + ) + + if exist "!zLibArchiveName!.zip" ( + echo Expanding ZLib... + if "!tarExists!" == "true" ( + if not exist "!zLibArchiveName!" mkdir "!zLibArchiveName!" > NUL + copy !zLibArchiveName!.zip !zLibArchiveName!\ > NUL + pushd !zLibArchiveName! > NUL + tar -xf "!zLibArchiveName!.zip" > nul 2>nul + del !zLibArchiveName!.zip > NUL + popd > NUL + ) else ( + powershell -command "Expand-Archive -Path '!zLibArchiveName!.zip' -DestinationPath '!zLibArchiveName!' -Force" + ) + + echo Installing ZLib... + if not exist "C:\Program Files\NVIDIA\CUDNN\v!version!\zlib" mkdir "C:\Program Files\NVIDIA\CUDNN\v!version!\zlib" + robocopy /e "!zLibArchiveName! " "C:\Program Files\NVIDIA\CUDNN\v!version!\zlib\ " /MOVE /NC /NS /NJS /NJH > NUL + + set zLibInstalled=true + ) + + if /i "!dryRun!" == "false" ( + REM We need to set the PATH variable. Some caveats: + REM 1. When you set the PATH variable, %PATH% will not reflect the update you just made, so + REM doing "PATH = PATH + change1" followed by "PATH = PATH + change2" results in just Change2 + REM being added. So: do all the changes in one fell swoop. + REM 2. We can't use setx /M PATH "%PATH%;C:\Program Files\NVIDIA\CUDNN\v!version!\zlib\dll_x64" + REM because setx truncates the path to 1024 characters. In 2022. Insanity. + REM 3. Only update the path if we need to. Check for existence before modifying! + + echo Updating PATH environment variable... + + set newPath=!PATH! + + REM Add ZLib path if it hasn't already been added + if "!zLibInstalled!" == "true" ( + if /i "!PATH:C:\Program Files\NVIDIA\CUDNN\v!version!\zlib=!" == "!PATH!" ( + set newPath=!newPath!;C:\Program Files\NVIDIA\CUDNN\v!version!\zlib\dll_x64 + ) + ) + REM Add cuDNN path if it hasn't already been added + if /i "!PATH:C:\Program Files\NVIDIA\CUDNN\v!version!\bin=!" == "!PATH!" ( + set newPath=!newPath!;C:\Program Files\NVIDIA\CUDNN\v!version!\bin + ) + + if /i "!newPath" NEQ "!PATH!" ( + rem echo New Path is !newPath! + powershell -command "[Environment]::SetEnvironmentVariable('PATH', '!newPath!','Machine'); + ) + ) + + echo done. + + REM Only process the first archive we find + call :InstallChecks + ) + ) +) + +goto:eof + +:InstallChecks + + if /i "!cuDNNInstalled!" == "false" ( + echo No cuDNN archive found. + echo Please download CUDA 11 from !cuDNNLocation! + pause + goto:eof + ) else if /i "!zLibInstalled!" == "false" ( + echo No ZLib found. + echo Please download ZLib from !zLibLocation!!zLibArchiveName!.zip + pause + goto:eof + ) + + exit /b + +:GetCudaVersion + + rem setlocal enabledelayedexpansion + + :: Use nvcc to find the CUDA version + where nvcc >nul 2>&1 + if !errorlevel! == 0 ( + REM Get the line containing "release x.y" + for /f "tokens=*" %%i in ('nvcc --version ^| findstr /C:"release"') do set cudaLine=%%i + REM Get the 5th token in the line when split by , and spaces + for /f "tokens=5 delims=, " %%a in ("!cudaLine!") do set cuda_version=%%a + ) else ( + REM Backup attempt: Use nvidia-smi to find the CUDA version + where nvidia-smi >nul 2>&1 + if !errorlevel! == 0 ( + REM Get the line containing "CUDA Version x.y" + for /f "tokens=*" %%i in ('nvidia-smi ^| findstr /C:"CUDA Version"') do set cudaLine=%%i + REM Get the 9th token in the line when split by spaces + for /f "tokens=9 delims= " %%a in ("!cudaLine!") do set cuda_version=%%a + ) else ( + REM echo Unable to find nvcc or nvidia-smi + ) + ) + + REM echo cudaLine = !cudaLine! + REM echo GetCudaVersion version: !cuda_version! + + if "!cuda_version!" neq "" ( + for /f "tokens=1,2 delims=." %%a in ("!cuda_version!") do ( + set "cuda_major_version=%%a" + exit /b + ) + ) + + REM pass back values as in params 1 and 2 + REM set "%~1=!cuda_version!" + REM set "%~2=!cuda_major_version!" + + exit /b diff --git a/devops/install/install_cuDNN.sh b/devops/install/install_cuDNN.sh index d3730197..4a169fcc 100644 --- a/devops/install/install_cuDNN.sh +++ b/devops/install/install_cuDNN.sh @@ -37,13 +37,17 @@ # sudo apt-get update -y # sudo apt-get -y install cuda-X.Y +if [ "$1" == "" ]; then + echo "Please provide the CUDA version for which to install cuDNN" + exit +fi # cuda_version=$1 # Get major.minor CUDA version cuda_version=$(cut -d '.' -f 1,2 <<< "$1") # This script is intended to be called from setup.sh, which includes architecture -# and os vars as well as writeline methods. If we don't find them, do quick checks +# and os vars as well as writeLine methods. If we don't find them, do quick checks if [[ $(type -t writeLine) != function ]]; then @@ -97,22 +101,26 @@ writeLine "Setting up CUDA ${cuda_version} and cuDNN" $color_info # ============================================================================== # GET SETTINGS -cuda_GPGpublicKey="" +cuda_GPGpublicKey="3bf863cc" case "$cuda_version" in - "12.2") cuda_version_full="12.2.1"; cuda_GPGpublicKey="3bf863cc" ;; - "12.1") cuda_version_full="12.1.1"; cuda_GPGpublicKey="3bf863cc" ;; - "12.0") cuda_version_full="12.0.1"; cuda_GPGpublicKey="3bf863cc" ;; - "11.8") cuda_version_full="11.8.0"; cuda_GPGpublicKey="3bf863cc" ;; - "11.7") cuda_version_full="11.7.1"; cuda_GPGpublicKey="3bf863cc" ;; - "11.6") cuda_version_full="11.6.2"; cuda_GPGpublicKey="3bf863cc" ;; - "11.5") cuda_version_full="11.5.2"; cuda_GPGpublicKey="3bf863cc" ;; - "11.4") cuda_version_full="11.4.4"; cuda_GPGpublicKey="7fa2af80" ;; - "11.3") cuda_version_full="11.3.1"; cuda_GPGpublicKey="7fa2af80" ;; - "11.2") cuda_version_full="11.2.2"; cuda_GPGpublicKey="7fa2af80" ;; - "11.1") cuda_version_full="11.1.1"; cuda_GPGpublicKey="7fa2af80" ;; - "11.0") cuda_version_full="11.0.1"; cuda_GPGpublicKey="7fa2af80" ;; - "10.2") cuda_version_full="10.2.89"; cuda_GPGpublicKey="7fa2af80" ;; + "12.6") cuda_version_full="12.6.1"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "12.5") cuda_version_full="12.5.1"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "12.5") cuda_version_full="12.4.1"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "12.3") cuda_version_full="12.3.2"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "12.2") cuda_version_full="12.2.2"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "12.1") cuda_version_full="12.1.1"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "12.0") cuda_version_full="12.0.1"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "11.8") cuda_version_full="11.8.0"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "11.7") cuda_version_full="11.7.1"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "11.6") cuda_version_full="11.6.2"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "11.5") cuda_version_full="11.5.2"; cuda_GPGpublicKey="3bf863cc" cudnn_version="8.9.5.*" ;; + "11.4") cuda_version_full="11.4.4"; cuda_GPGpublicKey="7fa2af80" cudnn_version="8.9.5.*" ;; + "11.3") cuda_version_full="11.3.1"; cuda_GPGpublicKey="7fa2af80" cudnn_version="8.9.5.*" ;; + "11.2") cuda_version_full="11.2.2"; cuda_GPGpublicKey="7fa2af80" cudnn_version="8.9.5.*" ;; + "11.1") cuda_version_full="11.1.1"; cuda_GPGpublicKey="7fa2af80" cudnn_version="8.9.5.*" ;; + "11.0") cuda_version_full="11.0.1"; cuda_GPGpublicKey="7fa2af80" cudnn_version="8.9.5.*" ;; + "10.2") cuda_version_full="10.2.89"; cuda_GPGpublicKey="7fa2af80" cudnn_version="8.9.5.*" ;; *) cuda_version_full="${cuda_version}.0" ;; esac diff --git a/devops/utils/compact_wsl_disks.ps1 b/devops/utils/compact_wsl_disks.ps1 new file mode 100644 index 00000000..ca45cca1 --- /dev/null +++ b/devops/utils/compact_wsl_disks.ps1 @@ -0,0 +1,55 @@ +# This script will search for WSL's VHDX files for and compact them + +# ENSURE: +# 1. You can run scripts: +# Set-ExecutionPolicy RemoteSigned -Scope CurrentUser +# 2. You have Hyper-V management tools: +# Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -All -NoRestart + +# Check if the script is running with administrator privileges +$IsAdmin = [bool]([System.Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid)) + +if (-not $IsAdmin) { + # Relaunch the script with administrator privileges + $argList = "$($myinvocation.MyCommand.Path)" # Get the script path + Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File $argList" -Verb RunAs + exit +} + +# Shutdown WSL +Write-Host "Shutting down WSL..." +wsl --shutdown +Start-Sleep -Seconds 5 # Wait a few seconds to ensure WSL is fully shut down +Write-Host "WSL shut down successfully." + +# Search for all VHDX files under LOCALAPPDATA\...\Packages\... +$baseFolderPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath "Packages" +$vdiskFiles = Get-ChildItem -Path $baseFolderPath -Recurse -Filter "ext4.vhdx" + +# Loop through each VHDX file +foreach ($vdisk in $vdiskFiles) { + + $diskName = $vdisk.Name + if ($vdisk.FullName -like "*Ubuntu*") { + $diskName = "Ubuntu VHDX" + } elseif ($vdisk.FullName -like "*Debian*") { + $diskName = "Debian VHDX" + } + + Write-Host "Found ${diskName} at $($vdisk.FullName)" + + # Ask the user if they want to compact this VHD/VHDX + $userInput = Read-Host "Do you want to compact this disk? (Y/N)" + + if ($userInput -match "^[Yy]$") { + # Compact the VHD/VHDX file to reclaim unused space + Optimize-VHD -Path $vdisk.FullName -Mode Full + Write-Host "VHD compacted: $($vdisk.Name)" + } + # else + # { + # Write-Host "Skipping VHD/VHDX: $($vdisk.Name)" + # } +} + +Write-Host "VHD/VHDX compression complete." diff --git a/docs/WSL-README.md b/docs/WSL-README.md index 448db674..1180f812 100644 --- a/docs/WSL-README.md +++ b/docs/WSL-README.md @@ -90,7 +90,7 @@ Windows. ### To free up space To free up space you can use the clean.bat/clean.sh scripts under -/src/SDK/Utilities. +/devops\install. For Windows ```cmd @@ -109,7 +109,8 @@ cleaned. To actually realise the freed up space in WSL you will need to compact the VHD in which your WSL instance resides. -In a Windows terminal: +You can use the `/devops/utils/compact_wsl_disks.ps1` script to achieve this. Or +to do it old school, in a Windows terminal: ```cmd wsl --shutdown @@ -131,5 +132,22 @@ detach vdisk exit ``` +For Debian, the name will be similar to `%LOCALAPPDATA%\Packages\TheDebianProject.DebianGNULinux_76v4gfsz19hv4\LocalState\ext4.vhdx`. + Your WSL virtual hard drive should be smaller and the space that was used -reclaimed by Windows. \ No newline at end of file +reclaimed by Windows. + +## GPU Support under WSL + +Please read [NVIDIAs guide](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#step-1-install-nvidia-driver-for-gpu-support). The main points are: + +1. **DO NOT install NVIDIA drivers within WSL**. The Windows drivers will work under WSL, so installing drivers *inside* WSL will overwrite the Windows drivers and lead to issues + +2. **DO install the NVIDIA toolkit separately in WSL**. Head to the [toolkit download page](https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=WSL-Ubuntu&target_version=2.0&target_type=deb_network) to find the correct instructions. For Windows 11 on CUDA 12.6, for example, the instructions are + + ``` bash + wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb + sudo dpkg -i cuda-keyring_1.1-1_all.deb + sudo apt-get update + sudo apt-get -y install cuda-toolkit-12-6 + ``` \ No newline at end of file diff --git a/modules/ObjectDetectionYOLOv5-6.2/install.sh b/modules/ObjectDetectionYOLOv5-6.2/install.sh index 082c6ef4..847fffbf 100644 --- a/modules/ObjectDetectionYOLOv5-6.2/install.sh +++ b/modules/ObjectDetectionYOLOv5-6.2/install.sh @@ -79,7 +79,7 @@ fi # OpenCV needs a specific version for macOS 11 # https://github.com/opencv/opencv-python/issues/777#issuecomment-1879553756 -if [ "$os_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x +if [ "$os_code_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x installPythonPackagesByName "opencv-python==4.6.0.66" "OpenCV 4.6.0.66 for macOS 11.x" fi diff --git a/modules/ObjectDetectionYOLOv5-6.2/requirements.linux.cuda11.txt b/modules/ObjectDetectionYOLOv5-6.2/requirements.linux.cuda11.txt index 05d96aac..bc45a753 100644 --- a/modules/ObjectDetectionYOLOv5-6.2/requirements.linux.cuda11.txt +++ b/modules/ObjectDetectionYOLOv5-6.2/requirements.linux.cuda11.txt @@ -9,11 +9,11 @@ Pillow<10.0.0 # Installing Pillow, a Python Image Library SciPy # Installing SciPy, a library for mathematics, science, and engineering PyYAML # Installing PyYAML, a library for reading configuration files -## For CUDA 11.7 (NOT torch 2.0+) ---find-links https://download.pytorch.org/whl/torch_stable.html -torch==1.13.0+cu117 # Installing PyTorch, an open source machine learning framework ---find-links https://download.pytorch.org/whl/torch_stable.html -torchvision==0.14.0+cu117 # Installing TorchVision, for working with computer vision models +## For CUDA 11.7 (will work in CUDA 11.8) +--extra-index-url https://download.pytorch.org/whl/cu117 +torch==1.13.1+cu117 # Installing PyTorch, an open source machine learning framework +--extra-index-url https://download.pytorch.org/whl/cu117 +torchvision==0.14.1+cu117 # Installing TorchVision, for working with computer vision models yolov5==6.2.3 # Installing Ultralytics YoloV5 package for object detection in images diff --git a/modules/ObjectDetectionYOLOv5Net/ObjectDetectionWorker.cs b/modules/ObjectDetectionYOLOv5Net/ObjectDetectionModuleRunner.cs similarity index 96% rename from modules/ObjectDetectionYOLOv5Net/ObjectDetectionWorker.cs rename to modules/ObjectDetectionYOLOv5Net/ObjectDetectionModuleRunner.cs index 544aa525..61e9ff66 100644 --- a/modules/ObjectDetectionYOLOv5Net/ObjectDetectionWorker.cs +++ b/modules/ObjectDetectionYOLOv5Net/ObjectDetectionModuleRunner.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Dynamic; using System.IO; using System.Linq; @@ -9,12 +10,13 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; -using CodeProject.AI.SDK; using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Backend; +using CodeProject.AI.SDK.Client; +using CodeProject.AI.SDK.Common; using CodeProject.AI.SDK.Utils; using Yolov5Net.Scorer; -using System.Dynamic; #pragma warning disable CS0162 // unreachable code @@ -27,7 +29,7 @@ namespace CodeProject.AI.Modules.ObjectDetection.YOLOv5 /// While intended for development and tests, this also demonstrates how a backend service can /// be created with the .NET Core framework. /// - public class ObjectDetectionWorker : ModuleWorkerBase + public class ObjectDetectionModuleRunner : ModuleRunnerBase { private const bool ShowTrace = false; @@ -49,20 +51,20 @@ public class ObjectDetectionWorker : ModuleWorkerBase /// The Logger. /// The app configuration values. /// The applicationLifetime object - public ObjectDetectionWorker(ILogger logger, IConfiguration config, + public ObjectDetectionModuleRunner(ILogger logger, IConfiguration config, IHostApplicationLifetime hostApplicationLifetime) : base(logger, config, hostApplicationLifetime) { _logger = logger; _mode = config.GetValue("MODEL_SIZE", "Medium") ?? "Medium"; - _modelDir = config.GetValue("MODELS_DIR", Path.Combine(moduleDirPath!, "assets")) ?? "assets"; - _customDir = config.GetValue("CUSTOM_MODELS_DIR", Path.Combine(moduleDirPath!, "custom-models")) ?? "custom-models"; + _modelDir = config.GetValue("MODELS_DIR", Path.Combine(ModuleDirPath!, "assets")) ?? "assets"; + _customDir = config.GetValue("CUSTOM_MODELS_DIR", Path.Combine(ModuleDirPath!, "custom-models")) ?? "custom-models"; - if (!_modelDir.EndsWith("/") || !_modelDir.EndsWith("\\")) + if (!_modelDir.EndsWith('/') || !_modelDir.EndsWith('\\')) _modelDir += "/"; - if (!_customDir.EndsWith("/") || !_customDir.EndsWith("\\")) + if (!_customDir.EndsWith('/') || !_customDir.EndsWith('\\')) _customDir += "/"; _modelDir = Text.FixSlashes(_modelDir); @@ -234,7 +236,7 @@ protected override int SelfTest() { RequestPayload payload = new RequestPayload("detect"); payload.SetValue("minconfidence", "0.4"); - payload.AddFile(Path.Combine(moduleDirPath!, "test/home-office.jpg")); + payload.AddFile(Path.Combine(ModuleDirPath!, "test/home-office.jpg")); var request = new BackendRequest(payload); ModuleResponse response = Process(request); diff --git a/modules/ObjectDetectionYOLOv5Net/ObjectDetectionYOLOv5Net.csproj b/modules/ObjectDetectionYOLOv5Net/ObjectDetectionYOLOv5Net.csproj index d7f4a462..f0d5700c 100644 --- a/modules/ObjectDetectionYOLOv5Net/ObjectDetectionYOLOv5Net.csproj +++ b/modules/ObjectDetectionYOLOv5Net/ObjectDetectionYOLOv5Net.csproj @@ -80,18 +80,21 @@ - - - + + + + + - + - + + diff --git a/modules/ObjectDetectionYOLOv5Net/ObjectDetector.cs b/modules/ObjectDetectionYOLOv5Net/ObjectDetector.cs index 0ca9726c..c488f0a1 100644 --- a/modules/ObjectDetectionYOLOv5Net/ObjectDetector.cs +++ b/modules/ObjectDetectionYOLOv5Net/ObjectDetector.cs @@ -9,8 +9,9 @@ using SkiaSharp; using Yolov5Net.Scorer; using Yolov5Net.Scorer.Models; -using CodeProject.AI.SDK; + using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Client; using CodeProject.AI.SDK.Utils; namespace CodeProject.AI.Modules.ObjectDetection.YOLOv5 diff --git a/modules/ObjectDetectionYOLOv5Net/Program.cs b/modules/ObjectDetectionYOLOv5Net/Program.cs index 1c9413cd..d539d706 100644 --- a/modules/ObjectDetectionYOLOv5Net/Program.cs +++ b/modules/ObjectDetectionYOLOv5Net/Program.cs @@ -8,12 +8,12 @@ internal static class Program { static async Task Main(string[]? args) { - ObjectDetectionWorker.ProcessArguments(args); + ObjectDetectionModuleRunner.ProcessArguments(args); IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { - services.AddHostedService(); + services.AddHostedService(); }) .Build(); diff --git a/modules/ObjectDetectionYOLOv5Net/install.sh b/modules/ObjectDetectionYOLOv5Net/install.sh index 311507e3..b05268c5 100644 --- a/modules/ObjectDetectionYOLOv5Net/install.sh +++ b/modules/ObjectDetectionYOLOv5Net/install.sh @@ -19,8 +19,12 @@ if [ "$1" != "install" ]; then exit 1 fi +installBinaries=false +if [ "$executionEnvironment" = "Production" ]; then installBinaries=true; fi +if [ "$launchedBy" = "server" ]; then installBinaries=true; fi + # Pull down the correct .NET image of ObjectDetectionYOLOv5Net based on this OS / GPU combo -if [ "${executionEnvironment}" = "Production" ] || [ "${launchedBy}" = "server" ]; then +if [ "${installBinaries}" = true ]; then imageName="ObjectDetectionYOLOv5Net-CPU-${moduleVersion}.zip" if [ "${installGPU}" = "true" ] && [ "${os}" != "macos" ]; then # Having issues with the OpenVINO version on linux @@ -29,6 +33,7 @@ if [ "${executionEnvironment}" = "Production" ] || [ "${launchedBy}" = "server" imageName="ObjectDetectionYOLOv5Net-CUDA-${moduleVersion}.zip" fi fi + writeLine "Pulling pre-build binary ${imageName}..." "$color_info" getFromServer "binaries/" "${imageName}" "bin" "Downloading ${imageName}..." else pushd "$moduleDirPath" >/dev/null diff --git a/modules/ObjectDetectionYOLOv5Net/modulesettings.json b/modules/ObjectDetectionYOLOv5Net/modulesettings.json index 0fb7ab9f..34991c9a 100644 --- a/modules/ObjectDetectionYOLOv5Net/modulesettings.json +++ b/modules/ObjectDetectionYOLOv5Net/modulesettings.json @@ -3,7 +3,7 @@ "ObjectDetectionYOLOv5Net": { "Name": "Object Detection (YOLOv5 .NET)", - "Version": "1.12.0", + "Version": "1.13.0", "PublishingInfo" : { "Description": "Provides Object Detection using YOLOv5 ONNX models with DirectML. This module is best for those on Windows and Linux without CUDA enabled GPUs", @@ -70,7 +70,8 @@ { "ModuleVersion": "1.10.1", "ServerVersionRange": [ "2.6.3", "2.7.0" ], "ReleaseDate": "2024-04-05", "ReleaseNotes": "Corrected reported Inference device" }, { "ModuleVersion": "1.10.2", "ServerVersionRange": [ "2.6.5", "2.7.0" ], "ReleaseDate": "2024-02-26", "ReleaseNotes": "Corrections for backwards compatibility for 2.6.5" }, { "ModuleVersion": "1.11.0", "ServerVersionRange": [ "2.8.0", "2.9.0" ], "ReleaseDate": "2024-08-02", "ReleaseNotes": "Updated for server 2.8" }, - { "ModuleVersion": "1.12.0", "ServerVersionRange": [ "2.9.1", "" ], "ReleaseDate": "2024-11-17", "ReleaseNotes": "Updated to .NET 9" } + { "ModuleVersion": "1.12.0", "ServerVersionRange": [ "2.9.1", "2.9.1" ], "ReleaseDate": "2024-11-17", "ReleaseNotes": "Updated to .NET 9" }, + { "ModuleVersion": "1.13.0", "ServerVersionRange": [ "2.9.2", "" ], "ReleaseDate": "2024-11-24", "ReleaseNotes": "Updated to reflect updated CodeProject.AI SDK" } ] }, diff --git a/src/SDK/NET/API/ApiClient.cs b/src/SDK/NET/API/ApiClient.cs index ced2a46d..1404cf74 100644 --- a/src/SDK/NET/API/ApiClient.cs +++ b/src/SDK/NET/API/ApiClient.cs @@ -1,4 +1,8 @@ -using System.Net.Http.Json; +using System; +using System.IO; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; using CodeProject.AI.SDK.Common; namespace CodeProject.AI.SDK.API diff --git a/src/SDK/NET/API/ModuleResponses.cs b/src/SDK/NET/API/ModuleResponses.cs index 6659855a..4bdf200b 100644 --- a/src/SDK/NET/API/ModuleResponses.cs +++ b/src/SDK/NET/API/ModuleResponses.cs @@ -1,4 +1,8 @@ using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +using CodeProject.AI.SDK.Backend; namespace CodeProject.AI.SDK.API { diff --git a/src/SDK/NET/API/ServerResponses.cs b/src/SDK/NET/API/ServerResponses.cs index 8b6fb207..4fea9fc1 100644 --- a/src/SDK/NET/API/ServerResponses.cs +++ b/src/SDK/NET/API/ServerResponses.cs @@ -1,8 +1,10 @@ -using System.Net; +using System; +using System.Collections.Generic; +using System.Net; using CodeProject.AI.SDK.Modules; +using CodeProject.AI.SDK.Client; using CodeProject.AI.SDK.Common; -using CodeProject.AI.SDK.Server; namespace CodeProject.AI.SDK.API { diff --git a/src/SDK/NET/Backend/BackendClient.cs b/src/SDK/NET/Backend/BackendClient.cs index 732afe16..4b570ad1 100644 --- a/src/SDK/NET/Backend/BackendClient.cs +++ b/src/SDK/NET/Backend/BackendClient.cs @@ -1,12 +1,16 @@ -using System.Diagnostics; +using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Dynamic; +using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; +using System.Threading; using System.Threading.Channels; - +using System.Threading.Tasks; using Microsoft.Extensions.Logging; -namespace CodeProject.AI.SDK +namespace CodeProject.AI.SDK.Backend { /// /// Represents an HTTP client for modules that gets requests and returns responses to the @@ -19,8 +23,9 @@ public class BackendClient private record LoggingData(string message, string category, LogLevel logLevel, string label); - private static HttpClient? _httpGetRequestClient; - private static HttpClient? _httpSendResponseClient; + private static HttpClient? _httpGetRequestClient; // For querying server's request queue + private static HttpClient? _httpSendResponseClient; // For sending response to request to server + private Channel _loggingQueue = Channel.CreateBounded(1024); private int _errorPauseSecs = 0; diff --git a/src/SDK/NET/Backend/BackendRequests.cs b/src/SDK/NET/Backend/BackendRequests.cs index 50fadbe2..78ea40f2 100644 --- a/src/SDK/NET/Backend/BackendRequests.cs +++ b/src/SDK/NET/Backend/BackendRequests.cs @@ -1,6 +1,9 @@ -using System.Text.Json.Serialization; +using System; +using System.Text.Json.Serialization; -namespace CodeProject.AI.SDK +using CodeProject.AI.SDK.Common; + +namespace CodeProject.AI.SDK.Backend { #pragma warning disable IDE1006 // Naming Styles diff --git a/src/SDK/NET/Backend/ModuleWorkerBase.cs b/src/SDK/NET/Backend/ModuleRunnerBase.cs similarity index 94% rename from src/SDK/NET/Backend/ModuleWorkerBase.cs rename to src/SDK/NET/Backend/ModuleRunnerBase.cs index 0856da42..6725ca5e 100644 --- a/src/SDK/NET/Backend/ModuleWorkerBase.cs +++ b/src/SDK/NET/Backend/ModuleRunnerBase.cs @@ -1,26 +1,33 @@ -using System.Diagnostics; +using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Dynamic; +using System.IO; +using System.Linq; +using System.Net.Http; using System.Net.Http.Json; - -using CodeProject.AI.SDK.API; -using CodeProject.AI.SDK.Utils; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace CodeProject.AI.SDK +using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Utils; + +namespace CodeProject.AI.SDK.Backend { /// /// The base class from which a module should be derived. /// - public abstract class ModuleWorkerBase : BackgroundService + public abstract class ModuleRunnerBase : BackgroundService { - private readonly string[] _doNotLogCommands = { + private readonly string[] _doNotLogCommands = [ "list-custom", "get_module_status", "status", "get_status", // status is deprecated alias "get_command_status" - }; + ]; private readonly TimeSpan _status_delay = TimeSpan.FromSeconds(2); @@ -55,7 +62,7 @@ public abstract class ModuleWorkerBase : BackgroundService /// /// Gets or sets the path to this Module /// - public string? moduleDirPath { get; set; } + public string? ModuleDirPath { get; set; } /// /// Gets or sets a value indicating whether or not this module supports GPU acceleration @@ -89,13 +96,13 @@ public abstract class ModuleWorkerBase : BackgroundService /// The Logger. /// The app configuration values. /// The applicationLifetime object - public ModuleWorkerBase(ILogger logger, IConfiguration configuration, + public ModuleRunnerBase(ILogger logger, IConfiguration configuration, IHostApplicationLifetime hostApplicationLifetime) { // _logger = logger; using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole()); - _logger = factory.CreateLogger(); + _logger = factory.CreateLogger(); _cancelled = false; _appLifetime = hostApplicationLifetime; @@ -106,23 +113,29 @@ public ModuleWorkerBase(ILogger logger, IConfiguration configuration, // TODO: We could load the settings directly from the modulesettings files rather than rely // rely environment variables: /* - // Load up the module's settings and start the module - var config = new ConfigurationBuilder(); - config.AddModuleSettingsConfigFiles(moduleDirPath, false); - IConfiguration configuration = config.Build(); + // Load up the modulesettings.*.json files + var configBuilder = new IConfigurationBuilder(); + configBuilder.AddModuleSettingsConfigFiles(ModuleDirPath, false); + IConfiguration config = configBuilder.Build(); // Bind the values in the configuration to a ModuleConfig object - string moduleId = [get module id from config] var moduleConfig = new ModuleConfig(); - configuration.Bind($"Modules:{moduleId}", moduleConfig); + try + { + config.Bind($"Modules:{moduleId}", moduleConfig); + } + catch (Exception) + { + moduleConfig = null; + } // Complete the ModuleConfig's setup. - if (moduleConfig.Initialise(moduleId, moduleDirPath, ModuleLocation.Internal)) + if (moduleConfig.Initialise(moduleId, ModuleDirPath, ModuleLocation.Internal)) */ _moduleId = configuration.GetValue("CPAI_MODULE_ID", null) ?? currentDirName; ModuleName = configuration.GetValue("CPAI_MODULE_NAME", null) ?? _moduleId; - moduleDirPath = configuration.GetValue("CPAI_MODULE_PATH", null) ?? currentModuleDirPath; + ModuleDirPath = configuration.GetValue("CPAI_MODULE_PATH", null) ?? currentModuleDirPath; _queueName = configuration.GetValue("CPAI_MODULE_QUEUENAME", null) ?? _moduleId.ToLower() + "_queue"; int port = configuration.GetValue("CPAI_PORT", 32168); @@ -632,9 +645,8 @@ protected string GetModuleDirectoryPath() string moduleDirPath = AppContext.BaseDirectory; DirectoryInfo? info = new DirectoryInfo(moduleDirPath); - // HACK: If we're running this server from the build output dir in dev environment - // then the root path will be wrong. - if (SystemInfo.IsDevelopmentCode) + // If we're in /bin/[Debug|Release]/netx.0 + if (SystemInfo.IsDevelopmentCode) { while (info != null) { @@ -646,6 +658,11 @@ protected string GetModuleDirectoryPath() } } } + else // are we in /bin? + { + if (info?.Name.ToLower() == "bin") + info = info.Parent; + } if (info != null) return info.FullName; diff --git a/src/SDK/NET/Common.targets b/src/SDK/NET/Common.targets index 4ffded86..9680e628 100644 --- a/src/SDK/NET/Common.targets +++ b/src/SDK/NET/Common.targets @@ -1,7 +1,7 @@  + + + + + + + + + + + + - + - - - - - - - + + - - + + + + + + + diff --git a/src/SDK/NET/Utils/ExpandoExtensions.cs b/src/SDK/NET/Utils/ExpandoExtensions.cs index 178a9207..832f1fa4 100644 --- a/src/SDK/NET/Utils/ExpandoExtensions.cs +++ b/src/SDK/NET/Utils/ExpandoExtensions.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Dynamic; using System.Reflection; using System.Text.Json; diff --git a/src/SDK/NET/Utils/ImageUtils.cs b/src/SDK/NET/Utils/ImageUtils.cs index b452a1b1..a11b7f7a 100644 --- a/src/SDK/NET/Utils/ImageUtils.cs +++ b/src/SDK/NET/Utils/ImageUtils.cs @@ -1,3 +1,4 @@ +using System.IO; using SkiaSharp; namespace CodeProject.AI.SDK.Utils diff --git a/src/SDK/NET/Utils/JsonUtils.cs b/src/SDK/NET/Utils/JsonUtils.cs index 8003ecf9..ae877dab 100644 --- a/src/SDK/NET/Utils/JsonUtils.cs +++ b/src/SDK/NET/Utils/JsonUtils.cs @@ -1,7 +1,11 @@ +using System; +using System.IO; +using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; +using System.Threading.Tasks; namespace CodeProject.AI.SDK.Utils { diff --git a/src/SDK/NET/Utils/ObjectPool.cs b/src/SDK/NET/Utils/ObjectPool.cs index 478fbe74..10890801 100644 --- a/src/SDK/NET/Utils/ObjectPool.cs +++ b/src/SDK/NET/Utils/ObjectPool.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; namespace CodeProject.AI.SDK.Utils { diff --git a/src/SDK/NET/Utils/SystemInfo.cs b/src/SDK/NET/Utils/SystemInfo.cs index a0ca2cdb..63240824 100644 --- a/src/SDK/NET/Utils/SystemInfo.cs +++ b/src/SDK/NET/Utils/SystemInfo.cs @@ -5,6 +5,11 @@ using System.Text.RegularExpressions; using Hardware.Info; +using System.Threading.Tasks; +using System.Collections.Generic; +using System; +using System.Linq; +using System.IO; #pragma warning disable CA1416 // Validate platform compatibility namespace CodeProject.AI.SDK.Utils @@ -246,6 +251,8 @@ public class SystemInfo private static string? _dockerContainerId; private static string? _osVersion; private static string? _osName; + private static string? _osFlavour; + private static string? _osCodeName; private static bool _isSSH; private static Runtimes _runtimes = new Runtimes(); @@ -611,7 +618,32 @@ public static string HardwareVendor } /// - /// Gets the current Operating System name. + /// Gets a value indicating whether the current OS is Windows + /// + public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + /// + /// Gets a value indicating whether the current OS is Linux + /// + public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + /// + /// Gets a value indicating whether the current OS is macOS + /// + public static bool IsMacOS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + /// + /// Gets a value indicating whether the current OS is FreeBSD + /// + public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); + + /// + /// Gets a value indicating whether we are currently running in Docker + /// + public static bool IsDocker => ExecutionEnvironment == ExecutionEnvironment.Docker; + + /// + /// Gets the Operating System type. Specifically: Windows, Linux, macOS or FreeBSD. /// public static string OperatingSystem { @@ -636,29 +668,20 @@ public static string OperatingSystem } /// - /// Gets a value indicating whether the current OS is Windows - /// - public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - /// - /// Gets a value indicating whether the current OS is Linux - /// - public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - - /// - /// Gets a value indicating whether the current OS is macOS - /// - public static bool IsMacOS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - - /// - /// Gets a value indicating whether the current OS is FreeBSD + /// Gets the actual name of the current Operating System name. eg Windows, Ubuntu, Debian. /// - public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); + public static string OperatingSystemName + { + get { return _osFlavour ?? string.Empty; } + } /// - /// Gets a value indicating whether we are currently running in Docker + /// Gets the current Operating System code name. eg Redstone, Jammy, Big Sur /// - public static bool IsDocker => ExecutionEnvironment == ExecutionEnvironment.Docker; + public static string OperatingSystemCodeName + { + get { return _osCodeName ?? string.Empty; } + } /// /// Returns the Operating System description, with corrections for Windows 11 @@ -667,26 +690,18 @@ public static string OperatingSystemDescription { get { - // WSL is Linux, so this isn't needed since the line below will handle it and do the - // same thing. However, it's here because we may want to report this differently. - if (IsWSL) - return $"{_osName} {_osVersion}"; - - if (IsLinux) - return $"{_osName} {_osVersion}"; + string description; + if (OperatingSystemName.StartsWith(OperatingSystem)) + description = $"{OperatingSystem} ({OperatingSystem} {OperatingSystemVersion}"; + else + description = $"{OperatingSystem} ({OperatingSystemName} {OperatingSystemVersion}"; - if (IsMacOS) - return $"{_osName} {_osVersion}"; + if (!OperatingSystemCodeName.StartsWith(OperatingSystem)) + description += $" {OperatingSystemCodeName}"; - // See https://github.com/getsentry/sentry-dotnet/issues/1484. - // C'mon guys: technically the version may be 10.x, but stick to the branding that - // the rest of the world understands. - if (IsWindows && - Environment.OSVersion.Version.Major >= 10 && - Environment.OSVersion.Version.Build >= 22000) - return RuntimeInformation.OSDescription.Replace("Windows 10.", "Windows 11 version 10."); + description += ")"; - return RuntimeInformation.OSDescription; + return description; } } @@ -731,7 +746,7 @@ public static async Task InitializeAsync() await CheckForWslAsync().ConfigureAwait(false); await GetDockerContainerIdAsync().ConfigureAwait(false); - await CheckOSVersionNameAsync().ConfigureAwait(false); + await GetOsDetailsAsync().ConfigureAwait(false); await CheckForSshAsync().ConfigureAwait(false); await GetcuDNNVersionAsync().ConfigureAwait(false); @@ -760,34 +775,34 @@ public static string GetSystemInfo() else info.AppendLine($"System: {SystemName}"); - info.AppendLine($"Operating System: {OperatingSystem} ({OperatingSystemDescription})"); + info.AppendLine($"Operating System: {OperatingSystemDescription}"); if (CPU is not null) { - var cpus = new StringBuilder(); + var cpuList = new StringBuilder(); if (!string.IsNullOrEmpty(CPU[0].Name)) { - cpus.Append(CPU[0].Name); + cpuList.Append(CPU[0].Name); if (!string.IsNullOrWhiteSpace(CPU[0].HardwareVendor)) - cpus.Append($" ({CPU[0].HardwareVendor})"); - cpus.Append("\n "); + cpuList.Append($" ({CPU[0].HardwareVendor})"); + cpuList.Append("\n "); } - cpus.Append(CPU.Count + " CPU"); + cpuList.Append(CPU.Count + " CPU"); if (CPU.Count != 1) - cpus.Append("s"); + cpuList.Append("s"); if (CPU[0].NumberOfCores > 0) { - cpus.Append($" x {CPU[0].NumberOfCores} core"); + cpuList.Append($" x {CPU[0].NumberOfCores} core"); if (CPU[0].NumberOfCores != 1) - cpus.Append("s"); + cpuList.Append("s"); } - cpus.Append("."); + cpuList.Append("."); if (CPU[0].LogicalProcessors > 0) - cpus.Append($" {CPU[0].LogicalProcessors} logical processors"); + cpuList.Append($" {CPU[0].LogicalProcessors} logical processors"); - info.AppendLine($"CPUs: {cpus} ({Architecture})"); + info.AppendLine($"CPUs: {cpuList} ({Architecture})"); } if (!string.IsNullOrWhiteSpace(gpuDesc)) @@ -1597,7 +1612,7 @@ private static async Task GetMemoryInfoAsync() private static CpuCollection GetCpuInfo() { - var cpus = new CpuCollection(); + var cpuList = new CpuCollection(); if (_hardwareInfo != null) { foreach (var cpu in _hardwareInfo.CpuList) @@ -1627,15 +1642,15 @@ private static CpuCollection GetCpuInfo() else if (EdgeDevice == "Radxa ROCK") cpuInfo.HardwareVendor = "Rockchip"; - cpus.Add(cpuInfo); + cpuList.Add(cpuInfo); } } // There's obviously at least 1. - if (cpus.Count == 0) - cpus.Add(new CpuInfo() { NumberOfCores = 1, LogicalProcessors = 1 }); + if (cpuList.Count == 0) + cpuList.Add(new CpuInfo() { NumberOfCores = 1, LogicalProcessors = 1 }); - return cpus; + return cpuList; } private async static Task CheckForWslAsync() @@ -1683,29 +1698,70 @@ private async static Task GetDockerContainerIdAsync() } } - private async static Task CheckOSVersionNameAsync() + private async static Task GetOsDetailsAsync() { // Default fallback _osName = OperatingSystem; + _osFlavour = OperatingSystem; _osVersion = Environment.OSVersion.Version.Major.ToString(); if (IsLinux) { - // Output is in the form: var results = await GetProcessInfoAsync("/bin/bash", "-c \". /etc/os-release;echo $NAME\"", null) - .ConfigureAwait(false); + .ConfigureAwait(false); if (results is not null) - _osName = results["output"]?.Trim(); // eg "ubuntu", "debian" + _osFlavour = results["output"]?.Trim(); // eg "ubuntu", "debian" + // VERSION_ID is in form "24.10" results = await GetProcessInfoAsync("/bin/bash", "-c \". /etc/os-release;echo $VERSION_ID\"", null) - .ConfigureAwait(false); + .ConfigureAwait(false); if (results is not null) _osVersion = results["output"]?.Trim(); // eg. "22.04" for Ubuntu 22.04, "12" for Debian 12 + + // VERSION is in form "24.10 (Oracular Oriole)" + var pattern = @"\((?[a-z\s]+)\)"; + var options = RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture; + results = await GetProcessInfoAsync("/bin/bash", "-c \". /etc/os-release;echo $VERSION\"", + pattern, options).ConfigureAwait(false); + if (results is not null) + { + string? name = results["name"]?.Trim(); // eg. "Oracular Oriole" for Ubuntu 24.10 + if (!string.IsNullOrWhiteSpace(name)) + _osCodeName = name; + } } else if (IsWindows) { - if (Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= 22000) - _osVersion = "11"; + _osCodeName = "Windows " + Environment.OSVersion.Version.Major; + + if (Environment.OSVersion.Version.Major >= 10) + { + // See https://github.com/getsentry/sentry-dotnet/issues/1484. + // C'mon guys: technically the version may be 10.x, but stick to the branding + // that the rest of the world understands. + if (Environment.OSVersion.Version.Build >= 22000) + { + _osVersion = "11"; + _osCodeName = "Sun Valley"; + + string command = "reg"; + string args = "query \"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\" /v DisplayVersion"; + string pattern = "DisplayVersion REG_SZ (?[A-Z\\d]+)"; + var options = RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture; + + var results = await GetProcessInfoAsync(command, args, pattern, options).ConfigureAwait(false); + if (results is not null) + { + string? version = results["version"]?.Trim(); // eg. "23H2" + if (!string.IsNullOrWhiteSpace(version)) + _osCodeName = version; + } + } + else + { + _osCodeName = "Redstone"; + } + } } else if (IsMacOS) { @@ -1713,7 +1769,7 @@ private async static Task CheckOSVersionNameAsync() var results = await GetProcessInfoAsync("/bin/bash", $"-c \"{command}\"", null) .ConfigureAwait(false); if (results is not null) - _osName = results["output"]?.Trim(); // eg. "Big Sur" + _osCodeName = results["output"]?.Trim(); // eg. "Big Sur" results = await GetProcessInfoAsync("/bin/bash", "-c \"sw_vers -productVersion\"", null) .ConfigureAwait(false); diff --git a/src/SDK/NET/Utils/Text.cs b/src/SDK/NET/Utils/Text.cs index 3b27c7f3..90cad767 100644 --- a/src/SDK/NET/Utils/Text.cs +++ b/src/SDK/NET/Utils/Text.cs @@ -1,4 +1,7 @@ -using System.Text.RegularExpressions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; namespace CodeProject.AI.SDK.Utils { diff --git a/src/SDK/Python/src/codeproject_ai_sdk/module_runner.py b/src/SDK/Python/src/codeproject_ai_sdk/module_runner.py index 5e9b7b08..fa655b31 100644 --- a/src/SDK/Python/src/codeproject_ai_sdk/module_runner.py +++ b/src/SDK/Python/src/codeproject_ai_sdk/module_runner.py @@ -1279,7 +1279,7 @@ def get_requirements_filepath(self) -> str: # then GPU, then CPU. With a query at each step for OS and architecture. filename = "" - os_name = self.system_info.os.lower() + os = self.system_info.os.lower() arch = self.system_info.cpu_arch.lower() if self.system_info.system == 'Raspberry Pi': @@ -1302,34 +1302,34 @@ def get_requirements_filepath(self) -> str: if self.system_info.hasTorchCuda: # TODO: Get the specific CUDA version and then add tests for .cudaMajor, .cudaMajor_Minor - if os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.{arch}.cuda.txt")): - filename = f"requirements.{os_name}.{arch}.cuda.txt" - elif os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.cuda.txt")): - filename = f"requirements.{os_name}.cuda.txt" + if os.path.exists(os.path.join(self.module_path, f"requirements.{os}.{arch}.cuda.txt")): + filename = f"requirements.{os}.{arch}.cuda.txt" + elif os.path.exists(os.path.join(self.module_path, f"requirements.{os}.cuda.txt")): + filename = f"requirements.{os}.cuda.txt" elif os.path.exists(os.path.join(self.module_path, f"requirements.cuda.txt")): filename = f"requirements.cuda.txt" if self.system_info.hasTorchROCm: - if os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.{arch}.rocm.txt")): - filename = f"requirements.{os_name}.{arch}.rocm.txt" - elif os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.rocm.txt")): - filename = f"requirements.{os_name}.rocm.txt" + if os.path.exists(os.path.join(self.module_path, f"requirements.{os}.{arch}.rocm.txt")): + filename = f"requirements.{os}.{arch}.rocm.txt" + elif os.path.exists(os.path.join(self.module_path, f"requirements.{os}.rocm.txt")): + filename = f"requirements.{os}.rocm.txt" elif os.path.exists(os.path.join(self.module_path, f"requirements.rocm.txt")): filename = f"requirements.rocm.txt" if not filename: - if os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.{arch}.gpu.txt")): - filename = f"requirements.{os_name}.{arch}.gpu.txt" - elif os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.gpu.txt")): - filename = f"requirements.{os_name}.gpu.txt" + if os.path.exists(os.path.join(self.module_path, f"requirements.{os}.{arch}.gpu.txt")): + filename = f"requirements.{os}.{arch}.gpu.txt" + elif os.path.exists(os.path.join(self.module_path, f"requirements.{os}.gpu.txt")): + filename = f"requirements.{os}.gpu.txt" elif os.path.exists(os.path.join(self.module_path, f"requirements.gpu.txt")): filename = f"requirements.gpu.txt" if not filename: - if os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.{arch}.txt")): - filename = f"requirements.{os_name}.{arch}.txt" - elif os.path.exists(os.path.join(self.module_path, f"requirements.{os_name}.txt")): - filename = f"requirements.{os_name}.txt" + if os.path.exists(os.path.join(self.module_path, f"requirements.{os}.{arch}.txt")): + filename = f"requirements.{os}.{arch}.txt" + elif os.path.exists(os.path.join(self.module_path, f"requirements.{os}.txt")): + filename = f"requirements.{os}.txt" elif os.path.exists(os.path.join(self.module_path, f"requirements.txt")): filename = f"requirements.txt" diff --git a/src/SDK/install.sh b/src/SDK/install.sh index 5ceb7e06..e25d0bd9 100644 --- a/src/SDK/install.sh +++ b/src/SDK/install.sh @@ -61,20 +61,21 @@ if [ "$os" = "linux" ]; then # - Needed for opencv-python (TODO: review these and move into module installers that actually use OpenCV) packages="ffmpeg libsm6 libxext6" - # - So we can query glxinfo for GPU info (mesa) and install modules (the rest) + # - So we can query glxinfo for GPU info (mesa) and install modules (the rest). + # NOTE: The general setup.sh file should have already installed curl and wget packages="${packages} mesa-utils curl rsync unzip wget" installAptPackages "${packages}" else if [ "${verbosity}" = "quiet" ]; then - if [ "$os_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x + if [ "$os_code_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x writeLine "** Installing System.Drawing support. On macOS 11 this could take a looong time" "$color_warn" else write "Installing System.Drawing support " fi - if [ $"$architecture" = 'arm64' ]; then + if [ "$architecture" = 'arm64' ]; then arch -x86_64 /usr/local/bin/brew list fontconfig >/dev/null 2>/dev/null || \ arch -x86_64 /usr/local/bin/brew install fontconfig >/dev/null 2>/dev/null & spin $! @@ -94,7 +95,7 @@ else else writeLine "Installing System.Drawing support " - if [ $"$architecture" = 'arm64' ]; then + if [ "$architecture" = 'arm64' ]; then arch -x86_64 /usr/local/bin/brew list fontconfig || arch -x86_64 /usr/local/bin/brew install fontconfig arch -x86_64 /usr/local/bin/brew list libomp || arch -x86_64 /usr/local/bin/brew install libomp else diff --git a/src/demos/clients/install.sh b/src/demos/clients/install.sh index cc0ac3f9..5b3c4eb9 100644 --- a/src/demos/clients/install.sh +++ b/src/demos/clients/install.sh @@ -30,7 +30,7 @@ setupPython # OpenCV needs a specific version for macOS 11 # https://github.com/opencv/opencv-python/issues/777#issuecomment-1879553756 -if [ "$os_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x +if [ "$os_code_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x installPythonPackagesByName "opencv-python==4.6.0.66" "OpenCV 4.6.0.66 for macOS 11.x" fi diff --git a/src/demos/modules/DotNetLongProcess/DotNetLongProcess.csproj b/src/demos/modules/DotNetLongProcess/DotNetLongProcess.csproj index ef6ce3f4..39257fea 100644 --- a/src/demos/modules/DotNetLongProcess/DotNetLongProcess.csproj +++ b/src/demos/modules/DotNetLongProcess/DotNetLongProcess.csproj @@ -56,9 +56,16 @@ ./LocalNugets + + + + + + + + - diff --git a/src/demos/modules/DotNetLongProcess/DotNetLongProcessWorker.cs b/src/demos/modules/DotNetLongProcess/DotNetLongProcessWorker.cs index 656a828d..081a8b7a 100644 --- a/src/demos/modules/DotNetLongProcess/DotNetLongProcessWorker.cs +++ b/src/demos/modules/DotNetLongProcess/DotNetLongProcessWorker.cs @@ -1,23 +1,19 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.IO; -using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; -using CodeProject.AI.SDK; using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Backend; +using CodeProject.AI.SDK.Common; using CodeProject.AI.SDK.Utils; -using SkiaSharp; -using System.Threading; - namespace CodeProject.AI.Modules.DotNetLongProcess { /// @@ -43,7 +39,7 @@ public class LongProcessModuleResponse : ModuleResponse /// While intended for development and tests, this also demonstrates how a backend service can /// be created with the .NET Core framework. /// - public class DotNetLongProcessWorker : ModuleWorkerBase + public class DotNetLongProcessWorker : ModuleRunnerBase { private const int _maxSteps = 10; @@ -69,7 +65,7 @@ public DotNetLongProcessWorker(ILogger logger, // Get some values from environment variables _modelSize = config.GetValue("MODEL_SIZE", "Medium") ?? "Medium"; - _modelDir = config.GetValue("MODELS_DIR", Path.Combine(moduleDirPath!, "assets")) ?? "assets"; + _modelDir = config.GetValue("MODELS_DIR", Path.Combine(ModuleDirPath!, "assets")) ?? "assets"; } protected override void Initialize() @@ -217,7 +213,7 @@ protected override int SelfTest() // Setup the request and add some test data RequestPayload payload = new RequestPayload("command"); payload.SetValue("minconfidence", "0.4"); - payload.AddFile(Path.Combine(moduleDirPath!, "test/home-office.jpg")); + payload.AddFile(Path.Combine(ModuleDirPath!, "test/home-office.jpg")); var request = new BackendRequest(payload); diff --git a/src/demos/modules/DotNetSimple/DotNetSimple.csproj b/src/demos/modules/DotNetSimple/DotNetSimple.csproj index 7c389c32..0628967e 100644 --- a/src/demos/modules/DotNetSimple/DotNetSimple.csproj +++ b/src/demos/modules/DotNetSimple/DotNetSimple.csproj @@ -48,22 +48,30 @@ - + + - - - - + + + - + + + + + + + + + diff --git a/src/demos/modules/DotNetSimple/DotNetSimpleWorker.cs b/src/demos/modules/DotNetSimple/DotNetSimpleWorker.cs index adf715a6..96c492f9 100644 --- a/src/demos/modules/DotNetSimple/DotNetSimpleWorker.cs +++ b/src/demos/modules/DotNetSimple/DotNetSimpleWorker.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; @@ -10,13 +9,16 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; -using CodeProject.AI.SDK; using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Backend; +using CodeProject.AI.SDK.Common; +using CodeProject.AI.SDK.Client; using CodeProject.AI.SDK.Utils; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; + using Yolov8Net; namespace CodeProject.AI.Modules.DotNetSimple @@ -38,7 +40,7 @@ public class ObjectDetectionResponse : ModuleResponse /// While intended for development and tests, this also demonstrates how a backend service can /// be created with the .NET Core framework. /// - public class DotNetSimpleWorker : ModuleWorkerBase + public class DotNetSimpleWorker : ModuleRunnerBase { private readonly ILogger _logger; private readonly string _modelSize; @@ -65,7 +67,7 @@ public DotNetSimpleWorker(ILogger logger, _logger = logger; _modelSize = config.GetValue("MODEL_SIZE", "Medium") ?? "Medium"; - _modelDir = config.GetValue("MODELS_DIR", Path.Combine(moduleDirPath!, "assets")) ?? "assets"; + _modelDir = config.GetValue("MODELS_DIR", Path.Combine(ModuleDirPath!, "assets")) ?? "assets"; if (!_modelDir.EndsWith("/") || !_modelDir.EndsWith("\\")) _modelDir += "/"; @@ -209,7 +211,7 @@ protected override int SelfTest() { RequestPayload payload = new RequestPayload("detect"); payload.SetValue("minconfidence", "0.4"); - payload.AddFile(Path.Combine(moduleDirPath!, "test/home-office.jpg")); + payload.AddFile(Path.Combine(ModuleDirPath!, "test/home-office.jpg")); var request = new BackendRequest(payload); diff --git a/src/demos/modules/DotNetSimple/install.bat b/src/demos/modules/DotNetSimple/install.bat index 0467aca2..28fb0294 100644 --- a/src/demos/modules/DotNetSimple/install.bat +++ b/src/demos/modules/DotNetSimple/install.bat @@ -1,6 +1,6 @@ :: Installation script ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: -:: .NET YOLO Object Detection +:: .NET Simple Demo :: :: This script is only called from ..\..\..\setup.bat :: @@ -12,20 +12,20 @@ @pause @goto:eof ) - -set installBinaries=false -if /i "!executionEnvironment!" == "Production" set installBinaries=true -if /i "!launchedBy!" == "server" set installBinaries=true - -:: Pull down the .NET executable of this module -if /i "!installBinaries!" == "true" ( - set imageName=!moduleId!-!moduleVersion!.zip - call "%utilsScript%" GetFromServer "binaries/" "!imageName!" "bin" "Downloading !imageName!..." +if /i "!executionEnvironment!" == "Production" ( + REM Often we just pull down the pre-compiled binaries from the CDN when in + REM production. This saves having to install the .NET SDK. This is a demo so + REM do nothing here. + call "!utilsScript!" WriteLine "Production install not supported" "!color_info!" ) else ( :: If we're in dev-setup mode we'll build the module now so the self-test will work pushd "!moduleDirPath!" call "!utilsScript!" WriteLine "Building project..." "!color_info!" - dotnet build -c Debug -o "!moduleDirPath!/bin/Debug/!dotNetTarget!" >NUL + if /i "%verbosity%" neq "quiet" ( + dotnet build -c Debug -o "!moduleDirPath!/bin/Debug/!dotNetTarget!" + ) else ( + dotnet build -c Debug -o "!moduleDirPath!/bin/Debug/!dotNetTarget!" >NUL + ) popd ) diff --git a/src/demos/modules/DotNetSimple/install.sh b/src/demos/modules/DotNetSimple/install.sh index f2109c4f..36946145 100644 --- a/src/demos/modules/DotNetSimple/install.sh +++ b/src/demos/modules/DotNetSimple/install.sh @@ -2,9 +2,9 @@ # Development mode setup script :::::::::::::::::::::::::::::::::::::::::::::: # -# .NET YOLO Object Detection +# .NET Simple Demo # -# This script is called from the ObjectDetectionYOLOv5Net directory using: +# This script is called from the DotNetSimple directory using: # # bash ../../../setup.sh # @@ -20,13 +20,23 @@ if [ "$1" != "install" ]; then fi # Pull down the correct .NET executable for this module -if [ "${executionEnvironment}" = "Production" ] || [ "${launchedBy}" = "server" ]; then - imageName="${moduleId}-${moduleVersion}.zip" - getFromServer "binaries/" "${imageName}" "bin" "Downloading ${imageName}..." +if [ "${executionEnvironment}" = "Production" ]; then + # Often we just pull down the pre-compiled binaries from the CDN when in + # production. This saves having to install the .NET SDK. This is a demo so + # do nothing here. + + # imageName="${moduleId}-${moduleVersion}.zip" + # getFromServer "binaries/" "${imageName}" "bin" "Downloading ${imageName}..." + + writeLine "Production install not supported." "$color_info" else pushd "$moduleDirPath" >/dev/null writeLine "Building project..." "$color_info" - dotnet build -c Debug -o "${moduleDirPath}/bin/Debug/${dotNetTarget}" >/dev/null + if [ $verbosity = "quiet" ]; then + dotnet build -c Debug -o "${moduleDirPath}/bin/Debug/${dotNetTarget}" >/dev/null + else + dotnet build -c Debug -o "${moduleDirPath}/bin/Debug/${dotNetTarget}" + fi popd >/dev/null fi diff --git a/src/demos/modules/PythonSimple/install.bat b/src/demos/modules/PythonSimple/install.bat index f709f5a1..80f5993b 100644 --- a/src/demos/modules/PythonSimple/install.bat +++ b/src/demos/modules/PythonSimple/install.bat @@ -1,6 +1,6 @@ :: Installation script ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: -:: Object Detection (YOLOv8) +:: Python Simple Demo :: :: This script is only called from ..\..\..\setup.bat :: diff --git a/src/demos/modules/PythonSimple/install.sh b/src/demos/modules/PythonSimple/install.sh index f12b91eb..ded1f2d4 100644 --- a/src/demos/modules/PythonSimple/install.sh +++ b/src/demos/modules/PythonSimple/install.sh @@ -2,9 +2,9 @@ # Development mode setup script :::::::::::::::::::::::::::::::::::::::::::::: # -# Object Detection (YOLOv8) +# Python Simple Demo # -# This script is called from the ObjectDetectionYOLOv8 directory using: +# This script is called from the /demos/modules/PythonSimple directory using: # # bash ../../../setup.sh # @@ -21,7 +21,7 @@ fi # OpenCV needs a specific version for macOS 11 # https://github.com/opencv/opencv-python/issues/777#issuecomment-1879553756 -if [ "$os_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x +if [ "$os_code_name" = "Big Sur" ]; then # macOS 11.x on Intel, kernal 20.x installPythonPackagesByName "opencv-python==4.6.0.66" "OpenCV 4.6.0.66 for macOS 11.x" fi diff --git a/src/demos/modules/PythonSimple/modulesettings.json b/src/demos/modules/PythonSimple/modulesettings.json index 1dd823d1..5aec045b 100644 --- a/src/demos/modules/PythonSimple/modulesettings.json +++ b/src/demos/modules/PythonSimple/modulesettings.json @@ -141,7 +141,7 @@ "Method": "POST", "Command": "custom", "MeshEnabled": false, - "Description": "Detects objects based on YOLO PyTorch models. Models are stored as .pt files in the /ObjectDetectionYOLOv8/assets directory, and to make a call to a specific model use /vision/custom/model-name, where 'model-name' is the name of the model's .pt file", + "Description": "Detects objects based on YOLO PyTorch models. Models are stored as .pt files in the /PythonSimple/assets directory, and to make a call to a specific model use /vision/custom/model-name, where 'model-name' is the name of the model's .pt file", "Inputs": [ { "Name": "image", diff --git a/src/demos/modules/PythonSimple/post_install.bat b/src/demos/modules/PythonSimple/post_install.bat index bcb64736..1388342e 100644 --- a/src/demos/modules/PythonSimple/post_install.bat +++ b/src/demos/modules/PythonSimple/post_install.bat @@ -1,6 +1,6 @@ :: Post-Installation script :::::::::::::::::::::::::::::::::::::::::::::::::::: :: -:: Object Detection (YOLOv8) +:: Python Simple :: :: The setup.bat file will find this post_install.bat file and execute it. :: diff --git a/src/demos/modules/PythonSimple/requirements.linux.cuda.txt b/src/demos/modules/PythonSimple/requirements.linux.cuda.txt index 74ac521a..554ca8cc 100644 --- a/src/demos/modules/PythonSimple/requirements.linux.cuda.txt +++ b/src/demos/modules/PythonSimple/requirements.linux.cuda.txt @@ -11,6 +11,6 @@ torchvision==0.14.0+cu117 # Installing TorchVision, for working with compu # Specific version because we have a patch ultralytics==8.1.2 # Installing Ultralytics package for object detection in images -CodeProject-AI-SDK # Installing the CodeProject.AI SDK +CodeProject-AI-SDK # Installing the CodeProject.AI SDK # last line empty.. \ No newline at end of file diff --git a/src/demos/modules/PythonSimple/requirements.linux.txt b/src/demos/modules/PythonSimple/requirements.linux.txt index eeeb4cdc..2b4fd730 100644 --- a/src/demos/modules/PythonSimple/requirements.linux.txt +++ b/src/demos/modules/PythonSimple/requirements.linux.txt @@ -12,4 +12,6 @@ torchvision==0.14.1+cpu # Installing TorchVision, for Computer Vision ba # Specific version because we have a patch ultralytics==8.1.2 # Installing Ultralytics package for object detection in images +CodeProject-AI-SDK # Installing the CodeProject.AI SDK + # last line empty. \ No newline at end of file diff --git a/src/demos/modules/PythonSimple/requirements.macos.txt b/src/demos/modules/PythonSimple/requirements.macos.txt index e220aedc..8b5fa407 100644 --- a/src/demos/modules/PythonSimple/requirements.macos.txt +++ b/src/demos/modules/PythonSimple/requirements.macos.txt @@ -5,6 +5,6 @@ numpy<2 # Installing NumPy, a package for scientific com # Specific version because we have a patch ultralytics==8.1.2 # Installing Ultralytics package for object detection in images -CodeProject-AI-SDK # Installing the CodeProject.AI SDK +CodeProject-AI-SDK # Installing the CodeProject.AI SDK # last line empty. \ No newline at end of file diff --git a/src/demos/modules/PythonSimple/requirements.windows.cuda.txt b/src/demos/modules/PythonSimple/requirements.windows.cuda.txt index efcc828f..48fc10f3 100644 --- a/src/demos/modules/PythonSimple/requirements.windows.cuda.txt +++ b/src/demos/modules/PythonSimple/requirements.windows.cuda.txt @@ -11,6 +11,6 @@ torchvision==0.14.0+cu117 # Installing TorchVision, for working with compu # Specific version because we have a patch ultralytics==8.1.2 # Installing Ultralytics package for object detection in images -CodeProject-AI-SDK # Installing the CodeProject.AI SDK +CodeProject-AI-SDK # Installing the CodeProject.AI SDK # last line empty. \ No newline at end of file diff --git a/src/scripts/utils.bat b/src/scripts/utils.bat index affef9c7..17642fee 100644 --- a/src/scripts/utils.bat +++ b/src/scripts/utils.bat @@ -127,13 +127,13 @@ shift & goto :%~1 :: Sets the currentColor global for the given foreground/background colors -:: currentColor must be output to the terminal before outputing text in +:: currentColor must be output to the terminal before outputting text in :: order to generate a colored output. :: :: string foreground color name. Optional if no background provided. :: Defaults to "White" :: string background color name. Optional. Defaults to Black. -:: string intense. Optional. If "true" then the insensity is turned up +:: string intense. Optional. If "true" then the intensity is turned up :setColor REM If you want to get a little fancy then you can also try @@ -343,7 +343,7 @@ shift & goto :%~1 if errorlevel 16 ( call :WriteLine "Failed" !color_error! else if errorlevel 8 ( - call :WriteLine "Some files inot copied" !color_warn! + call :WriteLine "Some files not copied" !color_warn! ) else ( call :WriteLine "done" !color_success! ) @@ -369,7 +369,7 @@ shift & goto :%~1 SetLocal EnableDelayedExpansion REM Param 1: The URL where the download can be found. - REM eg "https://codeproject-ai.s3.ca-central-1.amazonaws.com/server/models/" + REM eg "https://mycdn.com/server/models/" set assetStorageUrl=%1 set assetStorageUrl=!assetStorageUrl:"=! @@ -382,7 +382,8 @@ shift & goto :%~1 set downloadToDir=!downloadToDir:"=! REM Param 4: The name of the folder within the downloads directory where - REM the contents should be extracted. eg. assets + REM the contents should be extracted. eg. assets. + REM NOTE: If this param is empty then no extraction will happen set dirToExtract=%4 set dirToExtract=!dirToExtract:"=! @@ -393,22 +394,25 @@ shift & goto :%~1 if "!message!" == "" set message=Downloading !fileToGet!... if /i "%verbosity%" neq "quiet" ( - call :WriteLine "Downloading !fileToGet! from !assetStorageUrl! to !downloadToDir!\!dirToExtract!" "!color_info!" + call :WriteLine "Downloading !fileToGet! from !assetStorageUrl! to !downloadToDir!" "!color_info!" ) call :Write "!message!" "!color_primary!" - set extension=!fileToGet:~-3! - if /i "!extension!" NEQ ".gz" ( - set extension=!fileToGet:~-4! - if /i "!extension!" NEQ ".zip" ( - call :WriteLine "Unknown and unsupported file type for file !fileToGet!" "!color_error!" - exit /b REM no point in carrying on + REM If we're to extract this file then ensure it's an extractable file + if "!dirToExtract!" neq "" ( + set extension=!fileToGet:~-3! + if /i "!extension!" NEQ ".gz" ( + set extension=!fileToGet:~-4! + if /i "!extension!" NEQ ".zip" ( + call :WriteLine "Unknown and unsupported file type for file !fileToGet!" "!color_error!" + exit /b REM no point in carrying on + ) ) ) if /i "%verbosity%" neq "quiet" ( - call :WriteLine "Checking '!downloadToDir!\!fileToGet!'" "!color_info!" + call :WriteLine "Checking..." "!color_info!" ) if exist "!downloadToDir!\!fileToGet!" ( @@ -425,7 +429,7 @@ shift & goto :%~1 REM Be careful with the quotes so we can handle paths with spaces powershell -command "Start-BitsTransfer -Source '!assetStorageUrl!!fileToGet!' -Description !fileToGet! -Destination '!downloadToDir!\!fileToGet!'" - REM If these fail, it could be becuase of hanging transfers + REM If these fail, it could be because of hanging transfers if errorlevel 1 ( powershell -Command "Get-BitsTransfer | Remove-BitsTransfer" powershell -command "Start-BitsTransfer -Source '!assetStorageUrl!!fileToGet!' -Description !fileToGet! -Destination '!downloadToDir!\!fileToGet!'" @@ -453,18 +457,19 @@ shift & goto :%~1 call :WriteLine "Heading to !downloadToDir!" "!color_info!" ) - pushd "!downloadToDir!" - if not exist "!downloadToDir!\!dirToExtract!" mkdir "!downloadToDir!\!dirToExtract!" + REM Extract the file if we've been given an extraction folder name + if "!dirToExtract!" neq "" ( + pushd "!downloadToDir!" + if not exist "!downloadToDir!\!dirToExtract!" mkdir "!downloadToDir!\!dirToExtract!" - call :ExtractToDirectory "!fileToGet!" "!dirToExtract!" - - if errorlevel 1 ( + call :ExtractToDirectory "!fileToGet!" "!dirToExtract!" + if errorlevel 1 ( + popd + exit /b 1 + ) popd - exit /b 1 ) - popd - call :WriteLine "done." "!color_success!" exit /b @@ -518,6 +523,101 @@ shift & goto :%~1 exit /b +:EnsureVCRedistInstalled + SetLocal EnableDelayedExpansion + + set url_x64=https://aka.ms/vs/17/release/ + set file_x64=vc_redist.x64.exe + + call :Write "Checking for VC++ Redist..." %color_info% + reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\17.0\VC\Runtimes\x64" >nul 2>&1 + if %errorlevel% equ 0 ( + call :WriteLine "v17 Present." %color_success% + exit /b + ) + reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\16.0\VC\Runtimes\x64" >nul 2>&1 + if %errorlevel% equ 0 ( + call :WriteLine "v16 Present." %color_success% + exit /b + ) + reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\15.0\VC\Runtimes\x64" >nul 2>&1 + if %errorlevel% equ 0 ( + call :WriteLine "v15 Present." %color_success% + exit /b + ) + reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" >nul 2>&1 + if %errorlevel% equ 0 ( + call :WriteLine "v14 Present." %color_success% + exit /b + ) + + REM params: "https://cdn.com/assets" "file.zip" "\downloads\myModuleDir" "extract_dir" "Downloading files..." + REM Empty "extract_dir" means file will not be unzipped (since it's not a zip) + call :DownloadAndExtract "!url_x64!" "!file_x64!" "!downloadDirPath!\!platform!" "" "Downloading VC++ Redist files..." + + if exist "!downloadDirPath!\!platform!\!file_x64!" ( + call :Write "Installing VC++ Redist..." %color_info% + if /i "%verbosity%" == "quiet" ( + "!downloadDirPath!\!platform!\!file_x64!" /quiet /norestart + ) else ( + "!downloadDirPath!\!platform!\!file_x64!" /norestart + ) + call :WriteLine "Done." %color_info% + + REM -- Cleanup: Delete the installer files after installation + REM del /f /q "!downloadDirPath!\!platform!\!file_x64!" + ) else ( + call :WriteLine "Unable to download VC redist installer" !color_error! + ) + + exit /b + + +:EnsureWinGetInstalled + SetLocal EnableDelayedExpansion + + where winget >NUL 2>NUL + if errorlevel 1 ( + call :Write "Installing WinGet..." %color_info% + + set DoWingetInstall=true + + if "!DoWingetInstall!" == "true" ( + set "archType=x64" + if /i "!architecture!" == "arm64" set "archType=arm64" + + REM https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget-on-windows-sandbox + if /i "%verbosity%" == "quiet" ( + set progressType=silentlyContinue + ) else ( + set progressType=Continue + ) + powershell -command $progressPreference = '!progressType!'; ^ + Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle; ^ + Add-AppxPackage Microsoft.VCLibs.!archType!.14.00.Desktop.appx; + + powershell -command $progressPreference = '!progressType!'; ^ + Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.!archType!.14.00.Desktop.appx -OutFile Microsoft.VCLibs.!archType!.14.00.Desktop.appx; ^ + Add-AppxPackage Microsoft.UI.Xaml.2.8.!archType!.appx; + + powershell -command $progressPreference = '!progressType!'; ^ + Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.!archType!.appx -OutFile Microsoft.UI.Xaml.2.8.!archType!.appx; ^ + Add-AppxPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle; + + call :WriteLine "Done." %color_info% + + call :Write "Cleaning up..." %color_info% + del Microsoft.VCLibs.!archType!.14.00.Desktop.appx + del Microsoft.UI.Xaml.2.8.!archType!.appx + del Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle + call :WriteLine "Done." %color_info% + ) else ( + powershell -command "Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe" + ) + ) + + exit /b + :SetupDotNet SetLocal EnableDelayedExpansion @@ -640,37 +740,19 @@ shift & goto :%~1 ) ) else ( - where winget >NUL 2>NUL - if errorlevel 1 ( - call :Write "Installing WinGet..." %color_info% - - set "archType=x64" - if /i "!architecture!" == "arm64" set "archType=arm64" - - REM https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget-on-windows-sandbox - powershell -command ^ - $progressPreference = 'silentlyContinue'; ^ - Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle; ^ - Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.!archType!.14.00.Desktop.appx -OutFile Microsoft.VCLibs.!archType!.14.00.Desktop.appx; ^ - Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.!archType!.appx -OutFile Microsoft.UI.Xaml.2.8.!archType!.appx; ^ - Add-AppxPackage Microsoft.VCLibs.!archType!.14.00.Desktop.appx; ^ - Add-AppxPackage Microsoft.UI.Xaml.2.8.!archType!.appx; ^ - Add-AppxPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle; - - call :WriteLine "Done." %color_info% - - call :Write "Cleaning up..." %color_info% - del Microsoft.VCLibs.!archType!.14.00.Desktop.appx - del Microsoft.UI.Xaml.2.8.!archType!.appx - del Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle - call :WriteLine "Done." %color_info% - ) + call :EnsureWinGetInstalled if /i "!requestedType!" == "SDK" ( winget install Microsoft.DotNet.SDK.!requestedNetMajorVersion! ) else ( winget install Microsoft.DotNet.AspNetCore.!requestedNetMajorVersion! ) + + call :WriteLine "" + call :WriteLine "** You may need to restart this terminal (or VS Code if you're " %color_error% + call :WriteLine " in VS Code) and rerun setup.bat for the rest of this setup " %color_error% + call :WriteLine " script to work." %color_error% + call :WriteLine "" ) ) ) @@ -700,7 +782,7 @@ shift & goto :%~1 REM The path to the folder containing the base python installation set pythonRuntimeInstallPath=!runtimesDirPath!\bin\!os!\!pythonName! - REM For debugging, or correcting, we can force redownloads. Be careful though. + REM For debugging, or correcting, we can force re-downloads. Be careful though. if /i "%forceOverwrite%" == "true" ( REM Force Re-download @@ -711,7 +793,7 @@ shift & goto :%~1 REM Force overwrite of python installation call :WriteLine "Cleaning Python directory to force re-install of Python" "!color_info!" - call :WriteLine "This will mean any previous PIP installs wwill be lost." "!color_warn!" + call :WriteLine "This will mean any previous PIP installs will be lost." "!color_warn!" if exist "!pythonRuntimeInstallPath!" rmdir /s %rmdirFlags% "!pythonRuntimeInstallPath!" ) @@ -782,7 +864,7 @@ shift & goto :%~1 set searchDir=%~1 if /i "%verbosity%" neq "quiet" ( - call :WriteLine "Searching for a suitable requirements.txts file in !searchDir!" "!color_info!" + call :WriteLine "Searching for a suitable requirements.txt file in !searchDir!" "!color_info!" ) REM This is getting complicated. The order of priority for the requirements file is: @@ -822,7 +904,7 @@ shift & goto :%~1 if "!requirementsFilename!" == "" ( REM Unless installGPU is false, we are installing CUDA equipped packages - REM even if EnableGPU = false in the modulesettings files. This allows + REM even if installGPU = false in the modulesettings files. This allows REM you to toggle CUDA support at runtime, rather than install time. if /i "!installGPU!" == "true" ( if /i "!hasCUDA!" == "true" ( @@ -1276,7 +1358,7 @@ shift & goto :%~1 :GetCudaVersion - rem setlocal enabledelayedexpansion + REM SetLocal EnableDelayedExpansion :: Use nvcc to find the CUDA version where nvcc >nul 2>&1 @@ -1332,7 +1414,10 @@ shift & goto :%~1 if /i "!prevPart!" == "cuDNN" ( if /i "!part:~0,1!" == "v" ( set "cuDNN_version=!part:~1!" - REM @echo cuDNN version = !cuDNN_version! + + for /f "tokens=1 delims=." %%c in ("!cuDNN_version!") do ( set cuDNN_major_version=%%c ) + + REM @echo cuDNN version = !cuDNN_version! / !cuDNN_major_version! exit /b ) else ( set prevPart=!part! @@ -1361,7 +1446,7 @@ shift & goto :%~1 ) REM Module settings files are loaded in this order. Each file will overwrite (but not delete) - REM settings of the previous file. Becuase of this, we're going to search the files in REVERSE + REM settings of the previous file. Because of this, we're going to search the files in REVERSE REM order until we find the first value based on the most specific to least specific file. REM modulesettings.json REM modulesettings.development.json @@ -1428,9 +1513,9 @@ shift & goto :%~1 REM Gets a value from the modulesettings.json file (any JSON file, really) based -REM purely on the name of the propery. THIS METHOD DOES NOT TAKE INTO ACCOUNT THE +REM purely on the name of the property. THIS METHOD DOES NOT TAKE INTO ACCOUNT THE REM DEPTH OF A PROPERTY. If the property is at the root level or 10 levels down, -REM it's all the same. The extraction is done purely by grep/sed, so is very niaive. +REM it's all the same. The extraction is done purely by grep/sed, so is very naive. :GetValueFromModuleSettings jsonFilePath moduleId property returnValue set "moduleSettingValue=" SetLocal EnableDelayedExpansion @@ -1559,7 +1644,7 @@ REM it's all the same. The extraction is done purely by grep/sed, so is very nia exit /b -REM Gets the moduleID from a modulesettings.json file. See above function for commentss +REM Gets the moduleID from a modulesettings.json file. See above function for comments :GetModuleIdFromModuleSettingsFile jsonFilePath returnValue set "moduleSettingValue=" SetLocal EnableDelayedExpansion @@ -1745,7 +1830,7 @@ REM Gets the moduleID from a modulesettings.json file. See above function for c REM Strips single line comments from a file and stores the cleaned contents in a new file :StripJSONComments - setlocal enabledelayedexpansion + SetLocal EnableDelayedExpansion set inputFilePath=%~1 set cleanFilePath=%~2 @@ -1884,7 +1969,7 @@ REM Call this, then test: if "%online%" == "true" echo 'online' REM echo VERSION IS '%%a' set version=%%a ) - endlocal & set VCredistVersion=%version:v=% + EndLocal & set VCredistVersion=%version:v=% exit /b @@ -1893,7 +1978,7 @@ REM Get Windows Version :: Get the OS Name and version information :getWindowsOSName OSName - REM setlocal enabledelayedexpansion + REM SetLocal EnableDelayedExpansion REM Get the OS version information for /f "tokens=4-6 delims=. " %%i in ('ver') do ( @@ -1906,22 +1991,32 @@ REM Get Windows Version if "!ver_major!"=="6" ( if "!ver_minor!"=="1" ( set "windows_version=Windows 7" + set "os_code_name=Windows 7" ) else if "!ver_minor!"=="2" ( set "windows_version=Windows 8" + set "os_code_name=Windows 8" ) else if "!ver_minor!"=="3" ( set "windows_version=Windows 8.1" + set "os_code_name=Windows 8" ) else ( set "windows_version=Windows 6.x (Unknown Version)" + set "os_code_name=Windows" ) ) else if "!ver_major!"=="10" ( REM Check if it's Windows 10 or Windows 11 if !ver_build! GEQ 22000 ( set "windows_version=Windows 11" + set "os_code_name=Sun Valley" + for /f "tokens=2,*" %%A in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v DisplayVersion 2^>nul ^| find "DisplayVersion"') do ( + set os_code_name=%%B + ) ) else ( set "windows_version=Windows 10" + set "os_code_name=Redstone" ) ) else ( set "windows_version=Other Windows version" + set "os_code_name=Windows" ) REM Set return param value @@ -1945,7 +2040,7 @@ REM Thanks to https://stackoverflow.com/a/15809139/1128209 :: Nodes are normally strictly numeric, without a 0 prefix. A letter suffix :: is treated as a separate node :: - setlocal enableDelayedExpansion + SetLocal EnableDelayedExpansion set "v1=%~1" set "v2=%~2" @@ -2001,7 +2096,7 @@ REM Thanks to https://stackoverflow.com/a/15809139/1128209 :timeSince startTime duration - setlocal enableDelayedExpansion + SetLocal EnableDelayedExpansion set "startTime=%~1" diff --git a/src/scripts/utils.sh b/src/scripts/utils.sh index 50bd7d10..d3ba5b6c 100644 --- a/src/scripts/utils.sh +++ b/src/scripts/utils.sh @@ -29,14 +29,14 @@ function quit () { } # Returns a color code for the given foreground/background colors -# This code is echoed to the terminal before outputing text in +# This code is echoed to the terminal before outputting text in # order to generate a colored output. # # string foreground color name. Optional if no background provided. # Defaults to "Default" which uses the system default # string background color name. Optional. Defaults to "Default" # which is the system default -# string intense. Optional. If "true" then the insensity is turned up +# string intense. Optional. If "true" then the intensity is turned up # returns a string function Color () { @@ -291,10 +291,21 @@ function CreateWriteableDir () { else mkdir -p "${path}" >/dev/null 2>/dev/null fi - if [ $? -eq 0 ]; then + dir_error=$? + + if [ $dir_error = 1 ]; then + if [ "${verbosity}" = "loud" ]; then + sudo mkdir -p "${path}" + else + sudo mkdir -p "${path}" >/dev/null 2>/dev/null + fi + dir_error=$? + fi + + if [ $dir_error = 0 ]; then writeLine "done" $color_success else - writeLine "Needs admin permission to create folder" $color_error + writeLine "Needs admin permission to create folder (error $dir_error)" $color_error displayMacOSDirCreatePermissionError fi fi @@ -304,10 +315,11 @@ function CreateWriteableDir () { if [ "$isAdmin" = true ]; then write "Setting permissions on ${desc} folder..." $color_primary sudo chmod a+w "${path}" >/dev/null 2>/dev/null - if [ $? -eq 0 ]; then + dir_error=$? + if [ $dir_error = 0 ]; then writeLine "done" $color_success else - writeLine "Needs admin permission to set folder permissions" $color_error + writeLine "Needs admin permission to set folder permissions (error $dir_error)" $color_error fi fi fi @@ -347,7 +359,7 @@ function checkForAdminRights () { fi if [ "$isAdmin" = false ] && [ "$requestPassword" = true ]; then - if [ "$os" == "macos" ]; then + if [ "$os" = "macos" ]; then # THIS DOES NOT WORK # This shows the password prompt, but the admin rights starts and ends with the "whoami" # call. Once that call finishes, admin rights no longer apply. @@ -409,7 +421,7 @@ function checkForTool () { # Ensure Brew is installed. NOTE: macOS has curl built in, so no worries # about recursion if calling checkForTool "curl" - if [ $"$architecture" = 'arm64' ]; then + if [ "$architecture" = 'arm64' ]; then if [ ! -f /usr/local/bin/brew ]; then checkForAdminAndWarn "arch -x86_64 /bin/bash -c '$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)'" @@ -504,7 +516,7 @@ function checkForTool () { } function setupSSL() { - if [ "$os" = "linux" ] && [ "$architecture" == "x86_64" ]; then + if [ "$os" = "linux" ] && [ "$architecture" = "x86_64" ]; then if [ ! -f /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ] || [ ! -e /usr/lib/libcrypto.so.1.1 ]; then @@ -547,6 +559,132 @@ function setupSSL() { fi } + +function CheckAndSetupCUDA () { + + hasCUDA=false + cuDNN_version="" + cuda_major_version="" + cuda_major_minor="" + + if [ "$os" = "macos" ]; then + cuda_version="" + elif [ "${edgeDevice}" = "Jetson" ]; then + hasCUDA=true + cuda_version=$(getCudaVersion) + cuda_major_version=${cuda_version%%.*} + cuda_major_minor=$(echo "$cuda_version" | sed 's/\./_/g') + cuDNN_version=$(getcuDNNVersion) + + elif [ "${edgeDevice}" = "Raspberry Pi" ] || [ "${edgeDevice}" = "Orange Pi" ] || [ "${edgeDevice}" = "Radxa ROCK" ]; then + cuda_version="" + else + cuda_version=$(getCudaVersion) + cuda_major_version=${cuda_version%%.*} + cuda_minor_version=${cuda_version#*.} + cuda_major_minor=$(echo "$cuda_version" | sed 's/\./_/g') + + if [ "$cuda_version" != "" ]; then + + hasCUDA=true + cuDNN_version=$(getcuDNNVersion) + + installKeyring=false + if [ "$cuDNN_version" = "" ] || [ ! -x "$(command -v nvcc)" ]; then + writeLine "Installing NVIDIA keyring" $color_info + wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb > /dev/null 2>&1 & + spin $! + sudo dpkg -i cuda-keyring_1.1-1_all.deb >/dev/null & + spin $! + rm cuda-keyring_1.1-1_all.deb + fi + + if [ "$cuDNN_version" = "" ]; then + writeLine "Installing cuDNN" $color_info + # cuDNN + # https://developer.nvidia.com/cudnn-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_local + sudo apt-get update >/dev/null 2>&1 & + spin $! + sudo apt-get install "cudnn-cuda-$cuda_major_version" -y >/dev/null 2>&1 & + spin $! + fi + + if [ ! -x "$(command -v nvcc)" ]; then + # CUDA toolkit + # https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=WSL-Ubuntu&target_version=2.0&target_type=deb_network + writeLine "Installing CUDA toolkit" $color_info + + if [ "${os_name}" = "debian" ]; then + + # Backup existing sources.list + # echo "Backing up /etc/apt/sources.list to /etc/apt/sources.list.bak..." + # sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak + + main_repo="deb http://deb.debian.org/debian $os_code_name main" + if grep -qF "$main_repo contrib non-free" /etc/apt/sources.list; then + echo "Found '$main_repo'. Replacing it with '$main_repo contrib non-free'..." + sudo sed -i "s|^$main_repo|$main_repo contrib non-free|" /etc/apt/sources.list + else + echo "'$main_repo' not found. Adding '$main_repo contrib non-free' to sources.list..." + echo "$main_repo contrib non-free" | sudo tee -a /etc/apt/sources.list > /dev/null + fi + + src_repo="deb-src http://deb.debian.org/debian $os_code_name main" + if grep -qF "$src_repo contrib non-free" /etc/apt/sources.list; then + echo "Found '$src_repo'. Replacing it with '$src_repo contrib non-free'..." + sudo sed -i "s|^$src_repo|$updated_repo contrib non-free|" /etc/apt/sources.list + else + echo "'$src_repo' not found. Adding '$src_repo contrib non-free' to sources.list..." + echo "$src_repo contrib non-free" | sudo tee -a /etc/apt/sources.list > /dev/null + fi + fi + + sudo apt-get update > /dev/null 2>&1 & + spin $! + writeLine "Installing cuda kit" $color_info + sudo apt-get install cuda-toolkit-${cuda_major_version}-${cuda_minor_version} -y >/dev/null 2>&1 & + spin $! + fi + + # disable this + if [ "${systemName}" = "WSL-but-we're-ignoring-this-for-now" ]; then # we're disabling this on purpose + checkForAdminRights + if [ "$isAdmin" = false ]; then + writeLine "insufficient permission to install CUDA toolkit. Rerun under sudo" $color_error + else + # https://stackoverflow.com/a/66486390 + cp /usr/lib/wsl/lib/nvidia-smi /usr/bin/nvidia-smi > /dev/null 2>&1 + chmod a+x /usr/bin/nvidia-smi > /dev/null 2>&1 + + # Out of the box, WSL might come with CUDA 11.5, and no toolkit, + # meaning we rely on nvidia-smi for version info, which is wrong. + # We also should be thinking about CUDA 11.8 as a minimum, so let's + # upgrade. + cuda_comparison=$(versionCompare $cuda_version "11.8") + if [ "$cuda_comparison" = "-1" ]; then + writeLine "Upgrading WSL's CUDA install to 11.8" $color_info + + saveState + correctLineEndings "${installScriptsDirPath}/install_cuDNN.sh" + source "${installScriptsDirPath}/install_cuDNN.sh" 11.8 + restoreState + fi + fi + fi + + # We may have nvidia-smi, but not nvcc (eg in WSL). Fix this. + if [ -x "$(command -v nvidia-smi)" ] && [ ! -x "$(command -v nvcc)" ]; then + + installAptPackages "nvidia-cuda-toolkit" + + # The initial version we got would have been from nvidia-smi, which + # is wrong. Redo. + cuda_version=$(getCudaVersion) + fi + fi + fi +} + function getDotNetVersion() { local requestedType=$1 @@ -566,7 +704,7 @@ function getDotNetVersion() { # IFS=$'\n' # set the Internal Field Separator as end of line # while read -r line # do - # dotnet_version=$(echo "$line}" | cut -d ' ' -f 1) + # dotnet_version=$(echo "${line}" | cut -d ' ' -f 1) # current_comparison=$(versionCompare $dotnet_version $highestDotNetVersion) # # if (( $current_comparison > $comparison )); then @@ -584,26 +722,29 @@ function getDotNetVersion() { comparison=-1 - IFS=$'\n' # set the Internal Field Separator as end of line - while read -r line - do - if [[ ${line} == *'Microsoft.NETCore.App '* ]]; then - dotnet_version=$(echo "$line}" | cut -d ' ' -f 2) - # echo "GET: Found .NET runtime $dotnet_version" >&3 + if command -v dotnet >/dev/null 2>/dev/null; then + + IFS=$'\n' # set the Internal Field Separator as end of line + while read -r line + do + if [[ $line == *'Microsoft.NETCore.App '* ]]; then + dotnet_version=$(echo "${line}" | cut -d ' ' -f 2) + # echo "GET: Found .NET runtime $dotnet_version" >&3 - current_comparison=$(versionCompare $dotnet_version $highestDotNetVersion) - # echo "GET: current compare ${comparison}, new compare ${current_comparison}" >&3 + current_comparison=$(versionCompare $dotnet_version $highestDotNetVersion) + # echo "GET: current compare ${comparison}, new compare ${current_comparison}" >&3 - if (( $current_comparison >= $comparison )); then - highestDotNetVersion="$dotnet_version" - comparison=$current_comparison - # echo "GET: Found new highest .NET runtime $highestDotNetVersion" >&3 - # else - # echo "GET: Found $dotnet_version runtime, which is not higher than $highestDotNetVersion" >&3 + if (( $current_comparison >= $comparison )); then + highestDotNetVersion="$dotnet_version" + comparison=$current_comparison + # echo "GET: Found new highest .NET runtime $highestDotNetVersion" >&3 + # else + # echo "GET: Found $dotnet_version runtime, which is not higher than $highestDotNetVersion" >&3 + fi fi - fi - done <<< "$(dotnet --list-runtimes)" - unset IFS + done <<< "$(dotnet --list-runtimes)" + unset IFS + fi fi if [ "$highestDotNetVersion" = "0" ]; then highestDotNetVersion=""; fi @@ -619,6 +760,32 @@ function getMajorDotNetVersion() { echo "$dotnet_major_version" } +function setDotNetLocation () { + + local profile_location=$1 + local dotnet_path=$2 + + if [ -f $location ]; then + + if grep -q "export DOTNET_ROOT=${profile_location}"; then + write "Already added link to $profile_location" + else + write "Adding Link to $location" + echo 'export DOTNET_ROOT=${dotnet_path}' >> $profile_location + export DOTNET_ROOT=${dotnet_path} + fi + + if [[ $PATH == *"${dotnet_path}"* ]]; then + write "PATH contains location of .NET" + else + write "Adding location of .NET to PATH" + echo "export PATH=${dotnet_path}${PATH:+:${PATH}}" >> $profile_location + export PATH=${dotnet_path}${PATH:+:${PATH}} + fi + fi +} + + function setupDotNet () { # only major/minor versions accepted (eg 7.0) @@ -633,7 +800,7 @@ function setupDotNet () { if [ "$requestedType" = "" ]; then requestedType="aspnetcore"; fi - write "Checking for .NET ${requestedNetVersion}..." + write "Checking for .NET ${requestedNetVersion} ${requestedType}..." highestDotNetVersion="(None)" comparison=-1 @@ -648,8 +815,10 @@ function setupDotNet () { IFS=$'\n' # set the Internal Field Separator as end of line while read -r line do - dotnet_version=$(echo "$line}" | cut -d ' ' -f 1) - dotnet_major_version=$(echo "$dotnet_version}" | cut -d '.' -f 1) + # echo "SET: Read line $line" >&3 + + dotnet_version=$(echo "${line}" | cut -d ' ' -f 1) + dotnet_major_version=$(echo "${dotnet_version}" | cut -d '.' -f 1) # echo "SET: Found .NET SDK $dotnet_version" >&3 @@ -682,10 +851,10 @@ function setupDotNet () { IFS=$'\n' # set the Internal Field Separator as end of line while read -r line do - if [[ ${line} == *'Microsoft.NETCore.App '* ]]; then + if [[ $line == *'Microsoft.NETCore.App '* ]]; then - dotnet_version=$(echo "$line}" | cut -d ' ' -f 2) - dotnet_major_version=$(echo "$dotnet_version}" | cut -d '.' -f 1) + dotnet_version=$(echo "${line}" | cut -d ' ' -f 2) + dotnet_major_version=$(echo "${dotnet_version}" | cut -d '.' -f 1) # echo "SET: Found .NET runtime $dotnet_version" >&3 # Let's only compare major versions @@ -712,7 +881,7 @@ function setupDotNet () { fi mustInstall="false" - if [ "$haveRequested" == true ]; then + if [ "$haveRequested" = true ]; then writeLine "All good. .NET ${requestedType} is ${highestDotNetVersion}" $color_success elif (( $comparison == 0 )); then writeLine "All good. .NET ${requestedType} is ${highestDotNetVersion}" $color_success @@ -735,18 +904,25 @@ function setupDotNet () { return 6 # unable to download required asset fi - if [ "$architecture" = 'arm64' ]; then - dotnet_path="/opt/dotnet" - elif [ "$os" = "linux" ]; then - dotnet_path="/usr/lib/dotnet/" + if [ "$os" = "linux" ]; then + dotnet_basepath="/usr/lib/" else # macOS x64 - # dotnet_path="~/.dotnet/" - dotnet_path="/usr/local/share/dotnet/" + # dotnet_basepath="~/." + # if [ "$architecture" = 'arm64' ]; then + # dotnet_basepath="/opt/" + # else + dotnet_basepath="/usr/local/share/" + # fi fi + dotnet_path="${dotnet_basepath}/dotnet/" + useCustomDotNetInstallScript=false + # No longer using the arm64 custom script: standard install script seems good enough now. # output a warning message if no admin rights and instruct user on manual steps - if [ "$architecture" = 'arm64' ]; then - install_instructions="sudo bash '${installScriptsDirPath}/dotnet-install-arm.sh' $requestedNetMajorMinorVersion $requestedType" + # if [ "$architecture" = 'arm64' ]; then useCustomDotNetInstallScript=true; fi + + if [ "$useCustomDotNetInstallScript" = true ]; then + install_instructions="sudo bash '${installScriptsDirPath}/dotnet-install-arm.sh' $requestedNetMajorMinorVersion $requestedType" else install_instructions="sudo bash '${installScriptsDirPath}/dotnet-install.sh' --install-dir '${dotnet_path}' --channel $requestedNetMajorMinorVersion --runtime $requestedType" fi @@ -755,11 +931,18 @@ function setupDotNet () { if [ "$isAdmin" = true ] || [ "$attemptSudoWithoutAdminRights" = true ]; then if [ "$os" = "linux" ]; then - if [ "$os_name" = "debian-SKIP" ]; then + # Potentially not reliable. Only blessed by MS for Debian >= 12 + if [ "$os_name" = "debian" ] && [ ! "$os_vers" -lt "12" ]; then - wget https://packages.microsoft.com/config/debian/${os_vers}/packages-microsoft-prod.deb -O packages-microsoft-prod.deb >/dev/null - sudo dpkg -i packages-microsoft-prod.deb >/dev/null - rm packages-microsoft-prod.deb >/dev/null + if [ $verbosity = "quiet" ]; then + wget https://packages.microsoft.com/config/debian/${os_vers}/packages-microsoft-prod.deb -O packages-microsoft-prod.deb >/dev/null + sudo dpkg -i packages-microsoft-prod.deb >/dev/null + rm packages-microsoft-prod.deb >/dev/null + else + wget https://packages.microsoft.com/config/debian/${os_vers}/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + fi if [ "$requestedType" = "sdk" ]; then sudo apt-get update && sudo apt-get install -y dotnet-sdk-$requestedNetMajorMinorVersion >/dev/null @@ -767,8 +950,30 @@ function setupDotNet () { sudo apt-get update && sudo apt-get install -y aspnetcore-runtime-$requestedNetMajorMinorVersion >/dev/null fi + # .NET 9 seems to settle down the "we do/we don't" that MS is doing with .NET 6,7 and 8. + elif [ "$requestedNetMajorVersion" = "9" ]; then # ... && [ "$os_name" = "ubuntu" ] + + # TODO: Change this to a " >= 24.10" + if [ "$os_name" = "ubuntu" ] && [ "$os_vers" = "24.10" ]; then + if [ "$requestedType" = "sdk" ]; then + sudo apt-get update && sudo apt-get install -y dotnet-sdk-$requestedNetMajorMinorVersion >/dev/null + else + sudo apt-get update && sudo apt-get install -y aspnetcore-runtime-$requestedNetMajorMinorVersion >/dev/null + fi + else + # .NET 9 is still not fully released. But we know a guy who knows a guy... + # TODO: This also works for NET 6 & 7, Ubuntu 24.04, and .NET 9, Ubuntu 22.04 & 24.04 + sudo add-apt-repository ppa:dotnet/backports -y + sudo apt install "dotnet${requestedNetMajorVersion}" -y + fi + + # For everyone else, use The Script else - if [ "$architecture" = 'arm64' ]; then + + # Needed if we're installing .NET without an installer to help us + installAptPackages "ca-certificates libc6 libgcc-s1 libicu74 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g" + + if [ "$useCustomDotNetInstallScript" = true ]; then # installs in /opt/dotnet if [ $verbosity = "quiet" ]; then sudo bash "${installScriptsDirPath}/dotnet-install-arm.sh" "${requestedNetMajorVersion}.0" "$requestedType" "quiet" @@ -785,7 +990,13 @@ function setupDotNet () { fi fi fi + else + # macOS + + # (Maybe) needed if we're installing .NET without an installer to help us + # installAptPackages "ca-certificates libc6 libgcc-s1 libicu74 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g" + if [ $verbosity = "quiet" ]; then sudo bash "${installScriptsDirPath}/dotnet-install.sh" --install-dir "${dotnet_path}" --channel "$requestedNetMajorMinorVersion" --runtime "$requestedType" "--quiet" elif [ $verbosity = "loud" ]; then @@ -800,44 +1011,22 @@ function setupDotNet () { # return 2 # failed to install required runtime #fi - if [ "$os" = "linux" ] && [ "$architecture" != "arm64" ]; then - - # Add link - # ln -s /opt/dotnet/dotnet /usr/local/bin - - # if [ "$os_name" = "debian" ]; then - # sudo ln ~/.dotnet/dotnet /usr/bin - #fi - - # make link permanent - if grep -q 'export DOTNET_ROOT=' ~/.bashrc; then - echo 'Already added link to .bashrc' - else - echo 'export DOTNET_ROOT=/usr/bin/' >> ~/.bashrc - fi + # The install script is for CI/CD and doesn't actually register .NET. So add + # link and env variables + writeLine "Link Binaries to /usr/local/bin..." + if [ -e /usr/local/bin/dotnet ]; then + rm /usr/local/bin/dotnet fi + sudo ln -s ${dotnet_path}dotnet /usr/local/bin - if [ "$os" == "macos" ]; then - # The install script is for CI/CD and doesn't actually register .NET. So add - # link and env variable - export DOTNET_ROOT=${dotnet_path} - export PATH=${DOTNET_ROOT}${PATH:+:${PATH}} - - if [ ! -e /usr/local/bin/dotnet ]; then - ln -fs "${dotnet_path}dotnet" "/usr/local/bin/dotnet" - fi - - # if [ -f ~/.bashrc ]; then - if [ ! -f ~/.bashrc ] || [ $(grep -q 'export DOTNET_ROOT=' ~/.bashrc) ]; then - sudo echo "export DOTNET_ROOT=${dotnet_path}" >> ~/.bashrc - sudo echo "export PATH=${dotnet_path}${PATH:+:${PATH}}" >> ~/.bashrc - fi - # elif [ -f ~/.bash_profile ]; then - # if grep -q 'export DOTNET_ROOT=' ~/.bash_profile; then - # echo 'export DOTNET_ROOT=${dotnet_path}' >> ~/.bash_profile - # echo "export PATH=${dotnet_path}${PATH:+:${PATH}}" >> ~/.bash_profile - # fi - # fi + if [ -f " /home/pi/.bashrc" ]; then + setDotNetLocation " /home/pi/.bashrc" "${dotnet_path}" + elif [ -f " ~/.bashrc" ]; then + setDotNetLocation " ~/.bashrc" "${dotnet_path}" + elif [ -f " ~/.bash_profile" ]; then + setDotNetLocation " ~/.bash_profile" "${dotnet_path}" + elif [ -f " ~/.zshrc" ]; then + setDotNetLocation " ~/.zshrc" "${dotnet_path}" fi fi @@ -852,7 +1041,7 @@ function setupDotNet () { echo "sudo rm /etc/apt/sources.list.d/microsoft-prod.list" echo "sudo apt-get update" echo "# Install .NET SDK" - echo "sudo apt-get install dotnet-sdk-${requestedNetVersion}" + echo "sudo apt-get install dotnet-sdk-${requestedNetMajorMinorVersion}" else writeLine ".NET was not installed correctly. You may need to install .NET manually" $color_error writeLine "See https://learn.microsoft.com/en-us/dotnet/core/install/macos for downloads" $color_error @@ -1056,14 +1245,14 @@ function setupPython () { writeLine "done" $color_success - # macOS: With my M1 chip and Rosetta I make installing Python a real PITA. + # macOS: With my M1 chip and Rosetta, I make installing Python a real PITA. # Raspberry Pi: Hold my beer elif [ "${edgeDevice}" = "Raspberry Pi" ] || [ "${edgeDevice}" = "Orange Pi" ] || \ [ "${edgeDevice}" = "Radxa ROCK" ] || [ "${edgeDevice}" = "Jetson" ] || \ [ "$os_name" = "debian" ]; then # ensure gcc is installed - if [ "$os_name" == "debian" ]; then + if [ "$os_name" = "debian" ]; then # gcc and make installAptPackages "build-essential make" # to build python on Debian @@ -1086,7 +1275,7 @@ function setupPython () { fi fi - pushd "${appRootDirPath}" > /dev/null + pushd "${rootDirPath}" > /dev/null # Update at your leisure. # See https://www.python.org/ftp/python/ for a complete list. @@ -1095,15 +1284,17 @@ function setupPython () { "3.1") pythonPatchVersion="3.1.5";; "3.2") pythonPatchVersion="3.2.6";; "3.3") pythonPatchVersion="3.3.7";; - "3.4") pythonPatchVersion="3.4.19";; + "3.4") pythonPatchVersion="3.4.10";; "3.5") pythonPatchVersion="3.5.10";; "3.6") pythonPatchVersion="3.6.15";; "3.7") pythonPatchVersion="3.7.17";; - "3.8") pythonPatchVersion="3.8.18";; - "3.9") pythonPatchVersion="3.9.18";; - "3.10") pythonPatchVersion="3.10.13";; - "3.11") pythonPatchVersion="3.11.5";; - "3.12") pythonPatchVersion="3.12.0";; + "3.8") pythonPatchVersion="3.8.20";; + "3.9") pythonPatchVersion="3.9.20";; + "3.10") pythonPatchVersion="3.10.15";; + "3.11") pythonPatchVersion="3.11.10";; + "3.12") pythonPatchVersion="3.12.7";; + "3.13") pythonPatchVersion="3.13.0";; + "3.14") pythonPatchVersion="3.14.0";; *) pythonPatchVersion="${pythonVersion}.0" esac @@ -1126,9 +1317,9 @@ function setupPython () { # https://www.aliengen.com/blog/install-python-3-7-on-a-raspberry-pi-with-raspbian-8 # Download - cd $downloadDir - mkdir --parents "${os}/Lib" >/dev/null - cd "${os}/Lib" + cd "${downloadDirPath}" + mkdir --parents "${platform_dir}/lib" >/dev/null + cd "${os}/lib" if [ ! -f "openssl-1.1.1c.tar.gz" ]; then curl $curlFlags --remote-name https://www.openssl.org/source/openssl-1.1.1c.tar.gz @@ -1174,12 +1365,12 @@ function setupPython () { # Build and install Python cd Python-${pythonPatchVersion} - if [ "$os_name" == "debian" ]; then + if [ "$os_name" = "debian" ]; then # Native debian is giving us troubles. The instructions should be optimised down # to just what's needed, but for now we'll just throw everything at the problem # until we find a solution to the "SSLError("Can't connect to HTTPS URL because # the SSL module is not available.")' issue - sudo apt-get install libssl-dev libncurses5-dev libsqlite3-dev libreadline-dev libtk8.6 libgdm-dev libdb4o-cil-dev libpcap-dev + sudo apt-get install libssl-dev libncurses5-dev libsqlite3-dev libreadline-dev libtk8.6 libgdm-dev libdb4o-cil-dev libpcap-dev -y sudo ./configure --enable-optimizations make sudo make install @@ -1222,13 +1413,8 @@ function setupPython () { # For Linux we'll use apt-get the deadsnakes PPA to get the old version # of python. Deadsnakes? Old python? Get it? Get it?! And who said # developers have no sense of humour. - else - - # https://askubuntu.com/a/1481830 - # if [ "$os_name" = "debian" ]; then - # This allows adding the deadsnakes PPA, but this ppa doesn't support debian - # sudo apt-get install python3-launchpadlib -y - # fi + # NOTE: ppa is an Ubuntu thing only. https://askubuntu.com/a/1481830 + else # if [ "$os_name" = "ubuntu" ]; then if [ "${verbosity}" = "loud" ]; then @@ -1436,7 +1622,9 @@ function setupPython () { echo $pyVersion | grep "${pythonVersion}" >/dev/null if [ $? -ne 0 ]; then - errorNoPython + writeLine 'Not found' $color_error + # errorNoPython + return 1 fi writeLine 'All good' $color_success @@ -1721,9 +1909,6 @@ function installRequiredPythonPackages () { if [ "$installGPU" = "true" ]; then if [ "$cuda_version" != "" ]; then - - cuda_major_version=${cuda_version%%.*} - cuda_major_minor=$(echo "$cuda_version" | sed 's/\./_/g') if [ "${verbosity}" != "quiet" ]; then writeLine "CUDA version is $cuda_version (${cuda_major_minor} / ${cuda_major_version})" $color_info @@ -2299,6 +2484,10 @@ function getCudaVersion () { fi fi + cuda_major_version=${cuda_version%%.*} + cuda_minor_version=${cuda_version#*.} + cuda_major_minor=$(echo "$cuda_version" | sed 's/\./_/g') + echo $cuda_version } @@ -2319,7 +2508,7 @@ function getValueFromModuleSettingsFile () { local moduleId=$2 local property=$3 - if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" == "true" ]; then + if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" = "true" ]; then echo "Searching for '${property}' in a suitable modulesettings.json file in ${moduleDirPath}" >&3 fi @@ -2376,7 +2565,7 @@ function getValueFromModuleSettingsFile () { if [ "${moduleSettingValue}" != "" ]; then settings_file_used="modulesettings.json"; fi fi - if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" == "true" ]; then + if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" = "true" ]; then if [ "${moduleSettingValue}" = "" ]; then echo "Cannot find ${moduleId}.${property} in modulesettings in ${moduleDirPath}" >&3 else @@ -2429,7 +2618,7 @@ function getValueFromModuleSettings () { key=$".Modules.${moduleId}.${property}" fi - if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" == "true" ]; then + if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" = "true" ]; then echo jsonFile is $json_file >&3 echo parse_mode is $parse_mode >&3 fi @@ -2491,9 +2680,9 @@ function getValueFromModuleSettings () { fi # Really?? - if [ "$jsonValue" == "null" ]; then jsonValue=""; fi + if [ "$jsonValue" = "null" ]; then jsonValue=""; fi - if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" == "true" ]; then + if [ "$verbosity" = "loud" ] && [ "$debug_json_parse" = "true" ]; then echo "${key} = $jsonValue" >&3; fi @@ -2534,7 +2723,7 @@ function getModuleIdFromModuleSettings () { fi # Really?? A literal "null"? - if [ "$jsonValue" == "null" ]; then jsonValue=""; fi + if [ "$jsonValue" = "null" ]; then jsonValue=""; fi # debug # if [ "$verbosity" = "loud" ]; then echo "${key} = $jsonValue" >&3; fi @@ -2580,7 +2769,7 @@ function getValueFromJsonFile () { fi # Really?? A literal "null"? - if [ "$jsonValue" == "null" ]; then jsonValue=""; fi + if [ "$jsonValue" = "null" ]; then jsonValue=""; fi # debug # if [ "$verbosity" = "loud" ]; then echo "${key} = $jsonValue" >&3; fi @@ -2693,7 +2882,8 @@ function displayMacOSDirCreatePermissionError () { writeLine '' writeLine 'We may be able to suggest something:' $color_info - if [ "$os_name" = "Sonoma" ]; then # macOS 14 / Kernal 23 + # if [ "$os_name" = "Sonoma" ]; then # macOS 14 / Kernal 23 + if (( os_vers >= 13 )); then # Note that  will appear as the Apple symbol on macOS, but probably not on Windows or Linux writeLine '1. Pull down the  Apple menu and choose "System Settings"' writeLine '2. Choose “Privacy & Security"' @@ -2774,9 +2964,10 @@ fi if [[ $OSTYPE == 'darwin'* ]]; then platform='macos' os="macos" - os_name=$(awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | awk -F 'macOS ' '{print $NF}' | awk '{print substr($0, 0, length($0)-1)}') # eg "Big Sur" + os_name="macos" + os_code_name=$(awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | awk -F 'macOS ' '{print $NF}' | awk '{print substr($0, 0, length($0)-1)}') # eg "Big Sur" os_vers=$(sw_vers -productVersion) # eg "11.1" for macOS Big Sur - + platform_dir=$platform # or platform_dir="${platform// /}""; platform_dir="${platform_dir,,}" - no space, lowercase systemName=$os edgeDevice='' @@ -2785,8 +2976,10 @@ else os='linux' edgeDevice='' platform='linux' - os_name=$(. /etc/os-release;echo $ID) # eg "ubuntu", "debian12" + os_name=$(. /etc/os-release;echo $ID) # eg "ubuntu", "debian" os_vers=$(. /etc/os-release;echo $VERSION_ID) # eg "22.04" for Ubuntu 22.04, "12" for Debian 12 + os_code_name=$(. /etc/os-release;echo $VERSION_CODENAME) # eg "jammy" for Ubuntu 22.04, "bookworm" for Debian 12 + platform_dir=$os_name # or $platform if [ "$architecture" = 'arm64' ]; then platform='linux-arm64'; fi diff --git a/src/server/Backend/CommandDispatcher.cs b/src/server/Backend/CommandDispatcher.cs index 52cd3f6c..1a42f9c8 100644 --- a/src/server/Backend/CommandDispatcher.cs +++ b/src/server/Backend/CommandDispatcher.cs @@ -1,7 +1,8 @@ using System.Threading; using System.Threading.Tasks; -using CodeProject.AI.SDK; +using CodeProject.AI.SDK.Backend; +using CodeProject.AI.SDK.Common; namespace CodeProject.AI.Server.Backend { diff --git a/src/server/Backend/QueueServices.cs b/src/server/Backend/QueueServices.cs index 03a05975..b2f5be0c 100644 --- a/src/server/Backend/QueueServices.cs +++ b/src/server/Backend/QueueServices.cs @@ -10,8 +10,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using CodeProject.AI.SDK; using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Backend; namespace CodeProject.AI.Server.Backend { diff --git a/src/server/Controllers/ModuleController.cs b/src/server/Controllers/ModuleController.cs index c02a0993..9e54d776 100644 --- a/src/server/Controllers/ModuleController.cs +++ b/src/server/Controllers/ModuleController.cs @@ -4,17 +4,17 @@ using System.Linq; using System.Threading.Tasks; -using CodeProject.AI.SDK.API; -using CodeProject.AI.SDK.Modules; -using CodeProject.AI.SDK.Server; -using CodeProject.AI.Server.Models; -using CodeProject.AI.Server.Modules; - using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Client; +using CodeProject.AI.SDK.Modules; +using CodeProject.AI.Server.Models; +using CodeProject.AI.Server.Modules; + namespace CodeProject.AI.Server.Controllers { /// diff --git a/src/server/Controllers/ProxyController.cs b/src/server/Controllers/ProxyController.cs index 08bc0f56..06a50715 100644 --- a/src/server/Controllers/ProxyController.cs +++ b/src/server/Controllers/ProxyController.cs @@ -17,10 +17,10 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; -using CodeProject.AI.Server.Backend; -using CodeProject.AI.SDK; using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Common; using CodeProject.AI.SDK.Utils; +using CodeProject.AI.Server.Backend; using CodeProject.AI.Server.Modules; using CodeProject.AI.Server.Mesh; diff --git a/src/server/Controllers/QueueController.cs b/src/server/Controllers/QueueController.cs index 08d03935..9fb9e588 100644 --- a/src/server/Controllers/QueueController.cs +++ b/src/server/Controllers/QueueController.cs @@ -12,6 +12,7 @@ using CodeProject.AI.Server.Backend; using CodeProject.AI.SDK.Utils; using CodeProject.AI.Server.Modules; +using CodeProject.AI.SDK.Backend; namespace CodeProject.AI.Server.Controllers { diff --git a/src/server/Mesh/IMeshServerBroadcastBuilder.cs b/src/server/Mesh/IMeshServerBroadcastBuilder.cs deleted file mode 100644 index 95d617a9..00000000 --- a/src/server/Mesh/IMeshServerBroadcastBuilder.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace CodeProject.AI.Server.Mesh -{ - /// - /// This is used by the ServerMeshMonitor to build the status of the current node. - /// - public interface IMeshServerBroadcastBuilder where TStatus: MeshServerBroadcastData, new() - { - /// - /// Build the current node's status - /// - /// The current mesh monitor - /// The Mesh Node's status. - TStatus Build(BaseMeshMonitor meshMonitor); - } -} diff --git a/src/server/Mesh/MeshImplementation.cs b/src/server/Mesh/MeshImplementation.cs deleted file mode 100644 index 8a5705ad..00000000 --- a/src/server/Mesh/MeshImplementation.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Logging; - -using CodeProject.AI.Server.Modules; -using CodeProject.AI.SDK.Utils; -using CodeProject.AI.SDK.Modules; - -// ------------------------------------------------------------------------------------------------- -// This file implements the BaseMeshMonitor class and the IMeshServerBroadcastBuilder interface. -// ------------------------------------------------------------------------------------------------- - -namespace CodeProject.AI.Server.Mesh -{ - /// - /// The Mesh for the Servers. - /// - public class MeshMonitor : BaseMeshMonitor - { - /// - /// Creates a new instance of the Mesh class. - /// - /// The Mesh Options - /// The mesh node status builder. - /// A logger. - public MeshMonitor(IOptionsMonitor meshConfig, - MeshServerBroadcastBuilder statusBuilder, - ILogger logger) - : base(meshConfig, statusBuilder, logger) - { - } - } - - /// - /// Gets the nodes status to send to the Mesh. - /// - /// - /// The 'Build' method is abstracted using an interface to allow easier mocking and testing - /// - public class MeshServerBroadcastBuilder : IMeshServerBroadcastBuilder - { - private readonly ModuleCollection _modules; - private readonly ModuleProcessServices _processServices; - private readonly IOptionsMonitor _monitoredMeshOptions; - - /// - /// Initializes a new instance of the MeshNodeStatusBuilder class. - /// - /// The installed modules. - /// The MeshOptions monitoring object. - /// The Module Process Services. - public MeshServerBroadcastBuilder(IOptions modules, - IOptionsMonitor monitoredMeshOptions, // TODO: handle config changes - ModuleProcessServices processServices) - { - _modules = modules.Value; - _processServices = processServices; - _monitoredMeshOptions = monitoredMeshOptions; - } - - /// - /// Build the current node's broadcast package for mesh availability announcements - /// - /// A object - /// - /// REVIEW: This has now served it's purpose and is to be moved as a simple function into - /// MeshMonitor - /// - public MeshServerBroadcastData Build(BaseMeshMonitor meshMonitor) - { - // Get the running modules - IOrderedEnumerable runningModulesIds = _processServices - .ListProcessStatuses() - .Where(p => p.Status == ProcessStatusType.Started) - .Select(p => p.ModuleId) - .Distinct() - .OrderBy(m => m); - IEnumerable runningModules = _modules.Values - .Where(m => runningModulesIds.Contains(m.ModuleId)); - - // Get their routes - List enabledRoutes; - enabledRoutes = runningModules - .SelectMany(m => m.RouteMaps.Where(r => r.MeshEnabled ?? true) - .Select(r => r.Route)) - .Distinct() - .OrderBy(s => s) - .ToList(); - - // Hostnames of known servers in the mesh - var knownHostnames = meshMonitor.DiscoveredServers.Values - .Where(s => !s.Status.Hostname.EqualsIgnoreCase(meshMonitor.LocalHostname)) - .Select(s => s.Status.Hostname); - - // Description and platform - string systemDescription = $"{SystemInfo.SystemName} ({SystemInfo.OSAndArchitecture})"; - if (SystemInfo.GPU is not null) - { - if (SystemInfo.GPU.HardwareVendor == "Apple" || - SystemInfo.GPU.HardwareVendor == "NVIDIA" || - SystemInfo.GPU.HardwareVendor == "Intel") - { - systemDescription += " " + (SystemInfo.GPU.Name ?? "GPU"); - } - } - - string platform = SystemInfo.Platform; - if (SystemInfo.IsDocker) - platform = "Docker"; - - // For other settings - MeshOptions meshOptions = _monitoredMeshOptions.CurrentValue; - - // Now build. - return new MeshServerBroadcastData - { - Hostname = SystemInfo.MachineName, - SystemDescription = systemDescription, - Platform = platform, - EnabledRoutes = enabledRoutes, - KnownHostnames = knownHostnames, - IsBroadcasting = meshOptions.EnableStatusBroadcast, - IsMonitoring = meshOptions.EnableStatusMonitoring, - AcceptForwardedRequests = meshOptions.AcceptForwardedRequests, - AllowRequestForwarding = meshOptions.AllowRequestForwarding - }; - } - } -} diff --git a/src/server/Mesh/MeshManager.cs b/src/server/Mesh/MeshManager.cs index 5eee79fe..ceca5ed8 100644 --- a/src/server/Mesh/MeshManager.cs +++ b/src/server/Mesh/MeshManager.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Options; using CodeProject.AI.Server.Modules; -using CodeProject.AI.SDK.Common; using CodeProject.AI.SDK.Utils; /* ------------------------------------------------------------------------------------------------- @@ -31,11 +30,11 @@ namespace CodeProject.AI.Server.Mesh /// public class MeshManager { - private readonly MeshMonitor _meshMonitor; - private readonly IOptionsMonitor _meshOptions; - private readonly ServerSettingsJsonWriter _optionsJsonWriter; - private readonly ModuleProcessServices _processServices; - private readonly Task _updateRouteMetricsTask; + private readonly MeshMonitor _meshMonitor; + private readonly IOptionsMonitor _meshOptions; + private readonly ServerSettingsJsonWriter _optionsJsonWriter; + private readonly ModuleProcessServices _processServices; + private readonly Task _updateRouteMetricsTask; private readonly ConcurrentDictionary _routeMetrics = new(StringComparer.OrdinalIgnoreCase); @@ -68,10 +67,10 @@ public class MeshManager /// The Mesh Options Monitor /// Writes the Mesh JSON file. /// The process services. - public MeshManager(MeshMonitor meshMonitor, - IOptionsMonitor monitoredMeshOptions, - ServerSettingsJsonWriter optionsJsonWriter, - ModuleProcessServices processServices) + public MeshManager(MeshMonitor meshMonitor, + IOptionsMonitor monitoredMeshOptions, + ServerSettingsJsonWriter optionsJsonWriter, + ModuleProcessServices processServices) { _meshMonitor = meshMonitor; _meshOptions = monitoredMeshOptions; diff --git a/src/server/Mesh/MeshMonitor.cs b/src/server/Mesh/MeshMonitor.cs index c85a447d..66e6c854 100644 --- a/src/server/Mesh/MeshMonitor.cs +++ b/src/server/Mesh/MeshMonitor.cs @@ -15,6 +15,8 @@ using CodeProject.AI.SDK.Utils; using CodeProject.AI.SDK.API; +using CodeProject.AI.Server.Modules; +using CodeProject.AI.SDK.Modules; namespace CodeProject.AI.Server.Mesh { @@ -42,8 +44,7 @@ namespace CodeProject.AI.Server.Mesh /// status class and custom status builder. /// /// The Type of the status data included in the HEARTBEAT message. - public class BaseMeshMonitor - where TStatus: MeshServerBroadcastData, new() + public class MeshMonitor where TStatus: MeshServerBroadcastData, new() { private const string HeartBeatId = "HEARTBEAT"; private const string GoodByeId = "GOODBYE"; @@ -52,10 +53,14 @@ public class BaseMeshMonitor private const char Separator = '|'; private const string HostnameEnvVar = "COMPUTERNAME"; + private readonly JsonSerializerOptions _jsonSerializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + // Network clients private UdpClient? _udpClient = null; - private ApiClient? _pingClient = null; - private readonly List _knownServers = new(); // An address of this machine that is connected to the internet. It may not be the only // address, though @@ -64,19 +69,23 @@ public class BaseMeshMonitor // All addresses found on this machine. private readonly List _localIPAddresses = new(); - // dependencies - private readonly IOptionsMonitor _monitoredMeshOptions; private MeshOptions _oldMeshOptions; - private readonly IMeshServerBroadcastBuilder _statusBuilder; - private readonly ILogger> _logger; - private readonly ConcurrentDictionary _discoveredServers = new(); + // dependencies + private readonly IOptionsMonitor _monitoredMeshOptions; + private readonly ModuleProcessServices _processServices; + private readonly ILogger> _logger; - private readonly JsonSerializerOptions _jsonSerializerOptions = new() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; + // Currently running modules + private readonly ModuleCollection _modules; + + // Mesh servers we already know about + private readonly List _knownServers = new(); + // Mesh servers we've discovered + private readonly ConcurrentDictionary _discoveredServers = new(); + + // Process state private CancellationTokenSource? _cancellationTokenSource; private Task? _listenerTask; private Task? _pingServersTask; @@ -129,7 +138,7 @@ public class BaseMeshMonitor /// /// Gets the current mesh status /// - public TStatus MeshStatus => _statusBuilder.Build(this); + public MeshServerBroadcastData MeshStatus => GetMeshServerBroadcastData(); /// /// Gets the IPAddress of the local machine that we know is active. @@ -152,13 +161,15 @@ public class BaseMeshMonitor /// /// Creates a new instance of the BaseMeshMonitor class. /// - public BaseMeshMonitor(IOptionsMonitor meshConfig, - IMeshServerBroadcastBuilder statusBuilder, - ILogger> logger) + public MeshMonitor(IOptionsMonitor meshConfig, + IOptions modules, + ModuleProcessServices processServices, + ILogger> logger) { _monitoredMeshOptions = meshConfig; - _statusBuilder = statusBuilder; _logger = logger!; + _modules = modules.Value; + _processServices = processServices; _oldMeshOptions = meshConfig.CurrentValue; // Get the local address using a UDP socket. This is a better way to get the local @@ -206,6 +217,74 @@ public BaseMeshMonitor(IOptionsMonitor meshConfig, StartMonitoring(); } + /// + /// Build the current node's broadcast package for mesh availability announcements + /// + /// A object + /// + /// REVIEW: This has now served it's purpose and is to be moved as a simple function into + /// MeshMonitor + /// + public MeshServerBroadcastData GetMeshServerBroadcastData() + { + // Get the running modules + IOrderedEnumerable runningModulesIds = _processServices + .ListProcessStatuses() + .Where(p => p.Status == ProcessStatusType.Started) + .Select(p => p.ModuleId) + .Distinct() + .OrderBy(m => m); + IEnumerable runningModules = _modules.Values + .Where(m => runningModulesIds.Contains(m.ModuleId)); + + // Get their routes + List enabledRoutes; + enabledRoutes = runningModules + .SelectMany(m => m.RouteMaps.Where(r => r.MeshEnabled ?? true) + .Select(r => r.Route)) + .Distinct() + .OrderBy(s => s) + .ToList(); + + // Hostnames of known servers in the mesh + var knownHostnames = DiscoveredServers.Values + .Where(s => !s.Status.Hostname.EqualsIgnoreCase(LocalHostname)) + .Select(s => s.Status.Hostname); + + // Description and platform + string systemDescription = $"{SystemInfo.SystemName} ({SystemInfo.OSAndArchitecture})"; + if (SystemInfo.GPU is not null) + { + if (SystemInfo.GPU.HardwareVendor == "Apple" || + SystemInfo.GPU.HardwareVendor == "NVIDIA" || + SystemInfo.GPU.HardwareVendor == "Intel") + { + systemDescription += " " + (SystemInfo.GPU.Name ?? "GPU"); + } + } + + string platform = SystemInfo.Platform; + if (SystemInfo.IsDocker) + platform = "Docker"; + + // For other settings + MeshOptions meshOptions = _monitoredMeshOptions.CurrentValue; + + // Now build. + return new MeshServerBroadcastData + { + Hostname = SystemInfo.MachineName, + SystemDescription = systemDescription, + Platform = platform, + EnabledRoutes = enabledRoutes, + KnownHostnames = knownHostnames, + IsBroadcasting = meshOptions.EnableStatusBroadcast, + IsMonitoring = meshOptions.EnableStatusMonitoring, + AcceptForwardedRequests = meshOptions.AcceptForwardedRequests, + AllowRequestForwarding = meshOptions.AllowRequestForwarding + }; + } + /// /// Starts the service discovery and monitoring process. /// diff --git a/src/server/Mesh/MeshSupportExtensions.cs b/src/server/Mesh/MeshSupportExtensions.cs index c35e2338..baa1d55c 100644 --- a/src/server/Mesh/MeshSupportExtensions.cs +++ b/src/server/Mesh/MeshSupportExtensions.cs @@ -19,8 +19,7 @@ public static IServiceCollection AddMeshSupport(this IServiceCollection services { services.Configure(configuration.GetSection(nameof(MeshOptions))); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton>(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/server/Modules/LegacyModuleConfig.cs b/src/server/Modules/LegacyModuleConfig.cs index 658c94a7..fd2cf38b 100644 --- a/src/server/Modules/LegacyModuleConfig.cs +++ b/src/server/Modules/LegacyModuleConfig.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +using CodeProject.AI.SDK.Client; using CodeProject.AI.SDK.Modules; -using CodeProject.AI.SDK.Server; namespace CodeProject.AI.Server.Modules { diff --git a/src/server/Modules/ModuleCollection.cs b/src/server/Modules/ModuleCollection.cs index e9715bba..24f091f1 100644 --- a/src/server/Modules/ModuleCollection.cs +++ b/src/server/Modules/ModuleCollection.cs @@ -13,11 +13,11 @@ using Microsoft.Extensions.Configuration; using CodeProject.AI.SDK.API; +using CodeProject.AI.SDK.Client; using CodeProject.AI.SDK.Common; +using CodeProject.AI.SDK.Modules; using CodeProject.AI.SDK.Utils; using CodeProject.AI.Server.Models; -using CodeProject.AI.SDK.Server; -using CodeProject.AI.SDK.Modules; namespace CodeProject.AI.Server.Modules { @@ -271,7 +271,7 @@ public string? SettingsSummary return null; // Allow the module path to wrap. - // var path = moduleDirPath.Replace("\\", "\\"); + // var path = ModuleDirPath.Replace("\\", "\\"); // path = path.Replace("/", "/"); // or not... @@ -985,10 +985,11 @@ public async static Task CreateModulesListing(this ModuleCollection module Directory.CreateDirectory(dir); var corrections = new dynamic [] { - new { OldModuleId = "ObjectDetectionNet", NewModuleId = "ObjectDetectionYOLOv5Net" }, - new { OldModuleId = "ObjectDetectionYolo", NewModuleId = "ObjectDetectionYOLOv5-6.2" }, - new { OldModuleId = "Yolov5-3.1", NewModuleId = "ObjectDetectionYOLOv5-3.1" }, - new { OldModuleId = "TrainingYoloV5", NewModuleId = "TrainingObjectDetectionYOLOv5" } + // No longer including renamed versions + // new { OldModuleId = "ObjectDetectionNet", NewModuleId = "ObjectDetectionYOLOv5Net" }, + // new { OldModuleId = "ObjectDetectionYolo", NewModuleId = "ObjectDetectionYOLOv5-6.2" }, + // new { OldModuleId = "Yolov5-3.1", NewModuleId = "ObjectDetectionYOLOv5-3.1" }, + // new { OldModuleId = "TrainingYoloV5", NewModuleId = "TrainingObjectDetectionYOLOv5" } }; var moduleList = modules.Values @@ -1008,54 +1009,57 @@ public async static Task CreateModulesListing(this ModuleCollection module Downloads = 0 }).ToList(); - // Add renamed modules, using their new (current) names, but listing only server revisions v2.4+ - foreach (var pair in corrections) + if (corrections.Length > 0) { - ModuleConfig? module = modules.Values.Where(m => m.ModuleId == pair.NewModuleId).FirstOrDefault(); - if (module is not null) + // Add renamed modules, using their new (current) names, but listing only server revisions v2.4+ + foreach (var pair in corrections) { - ModuleRelease[] post24Releases = module.InstallOptions!.ModuleReleases - .Where(r => string.IsNullOrWhiteSpace(r.ServerVersionRange?[0]) || - VersionInfo.Compare(r.ServerVersionRange[0], "2.4") >= 0) - .ToArray(); - moduleList.Add(new { - ModuleId = module.ModuleId, - Name = module.Name, - Version = module.Version, - PublishingInfo = module.PublishingInfo, - InstallOptions = new { - Platforms = module.InstallOptions.Platforms, - ModuleReleases = post24Releases - }, - Downloads = 0 - }); + ModuleConfig? module = modules.Values.Where(m => m.ModuleId == pair.NewModuleId).FirstOrDefault(); + if (module is not null) + { + ModuleRelease[] post24Releases = module.InstallOptions!.ModuleReleases + .Where(r => string.IsNullOrWhiteSpace(r.ServerVersionRange?[0]) || + VersionInfo.Compare(r.ServerVersionRange[0], "2.4") >= 0) + .ToArray(); + moduleList.Add(new { + ModuleId = module.ModuleId, + Name = module.Name, + Version = module.Version, + PublishingInfo = module.PublishingInfo, + InstallOptions = new { + Platforms = module.InstallOptions.Platforms, + ModuleReleases = post24Releases + }, + Downloads = 0 + }); + } } - } - // Add renamed modules, but with their old names, and only up to server v2.4 - foreach (var pair in corrections) - { - ModuleConfig? module = modules.Values.Where(m => m.ModuleId == pair.NewModuleId).FirstOrDefault(); - if (module is not null) + // Add renamed modules, but with their old names, and only up to server v2.4 + foreach (var pair in corrections) { - ModuleRelease[] pre24Releases = module.InstallOptions!.ModuleReleases - .Where(r => string.IsNullOrWhiteSpace(r.ServerVersionRange?[0]) || - VersionInfo.Compare(r.ServerVersionRange[0], "2.4") < 0) - .ToArray(); - moduleList.Add(new { - ModuleId = (string?)pair.OldModuleId, - Name = module.Name, - Version = module.Version, - PublishingInfo = module.PublishingInfo, - InstallOptions = new { - Platforms = module.InstallOptions.Platforms, - ModuleReleases = pre24Releases - }, - Downloads = 0 - }); + ModuleConfig? module = modules.Values.Where(m => m.ModuleId == pair.NewModuleId).FirstOrDefault(); + if (module is not null) + { + ModuleRelease[] pre24Releases = module.InstallOptions!.ModuleReleases + .Where(r => string.IsNullOrWhiteSpace(r.ServerVersionRange?[0]) || + VersionInfo.Compare(r.ServerVersionRange[0], "2.4") < 0) + .ToArray(); + moduleList.Add(new { + ModuleId = (string?)pair.OldModuleId, + Name = module.Name, + Version = module.Version, + PublishingInfo = module.PublishingInfo, + InstallOptions = new { + Platforms = module.InstallOptions.Platforms, + ModuleReleases = pre24Releases + }, + Downloads = 0 + }); + } } } - + var options = new JsonSerializerOptions { WriteIndented = true }; string configJson = JsonSerializer.Serialize(moduleList, options); diff --git a/src/server/Modules/ModuleProcessServices.cs b/src/server/Modules/ModuleProcessServices.cs index 3aa496f6..65230aa1 100644 --- a/src/server/Modules/ModuleProcessServices.cs +++ b/src/server/Modules/ModuleProcessServices.cs @@ -15,6 +15,8 @@ using CodeProject.AI.Server.Backend; using CodeProject.AI.Server.Models; using CodeProject.AI.SDK.Modules; +using CodeProject.AI.SDK.Backend; +using CodeProject.AI.SDK.Common; namespace CodeProject.AI.Server.Modules { diff --git a/src/server/Modules/ModuleRunner.cs b/src/server/Modules/ModuleRunner.cs index b1057cc0..5f66e61a 100644 --- a/src/server/Modules/ModuleRunner.cs +++ b/src/server/Modules/ModuleRunner.cs @@ -52,7 +52,7 @@ public Dictionary? GlobalEnvironmentVariables /// public ModuleProcessServices ProcessService { get; } - private readonly MeshMonitor _meshMonitor; + private readonly MeshMonitor _meshMonitor; /// /// Gets a reference to the ModuleSettings object. @@ -83,7 +83,7 @@ public ModuleRunner(IOptions versionOptions, ModuleSettings moduleSettings, ModuleInstaller moduleInstaller, ModuleProcessServices processService, - MeshMonitor meshMonitor, + MeshMonitor meshMonitor, ILogger logger) { _versionConfig = versionOptions.Value; diff --git a/src/server/Modules/ModuleSettings.cs b/src/server/Modules/ModuleSettings.cs index 7398d907..b780b456 100644 --- a/src/server/Modules/ModuleSettings.cs +++ b/src/server/Modules/ModuleSettings.cs @@ -25,6 +25,7 @@ public class ModuleSettings const string CurrentModuleDirPathMarker = "%CURRENT_MODULE_PATH%"; const string PlatformMarker = "%PLATFORM%"; const string OSMarker = "%OS%"; + const string OSNameMarker = "%OS_NAME%"; const string DataDirMarker = "%DATA_DIR%"; const string PythonPathMarker = "%PYTHON_PATH%"; const string PythonNameMarker = "%PYTHON_NAME%"; @@ -180,14 +181,14 @@ public string GetFilePath(ModuleConfig module) // If it is a Python3X command then replace our marker in the default python path to // match the requested interpreter location in order to build the - // "/runtimes/bin/linux/python38/venv/bin/python3" path. + // "/runtimes/bin/ubuntu/python38/venv/bin/python3" path. if (runtime.StartsWith("python")) { // HACK: In Docker, Python installations for modules can be local for downloaded // modules, or shared for pre-installed modules. For preinstalled modules hardcoded // into the Docker image, the python runtimes and package are installs at the // system level, and not in a virtual environment. This means Python command is in - // the format of "python3.N" rather than "/runtimes/bin/linux/python3N/venv/bin/python3" + // the format of "python3.N" rather than "/runtimes/bin/ubuntu/python3N/venv/bin/python3" // ie. Python runtime location is 'System'. if (SystemInfo.IsDocker) { @@ -340,6 +341,7 @@ private void ExpandMacros() value = value.Replace(ExternalModulesDirPath, _moduleOptions.ExternalModulesDirPath); value = value.Replace(PlatformMarker, SystemInfo.Platform.ToLower()); value = value.Replace(OSMarker, SystemInfo.OperatingSystem.ToLower()); + value = value.Replace(OSNameMarker, SystemInfo.OperatingSystemName.ToLower()); value = value.Replace(PythonPathMarker, _moduleOptions.PythonRelativeInterpreterPath); value = value.Replace(DataDirMarker, _appDataDirectory); // Do this last in case other markers contains this marker diff --git a/src/server/Server.csproj b/src/server/Server.csproj index 171d50a2..a5f14909 100644 --- a/src/server/Server.csproj +++ b/src/server/Server.csproj @@ -79,25 +79,32 @@ https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=v - + - - + + + + + + + + + + + + - + + + + - - - - @@ -177,7 +184,7 @@ https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=v diff --git a/src/server/wwwroot/index.html b/src/server/wwwroot/index.html index 0d17c848..ad8edae7 100644 --- a/src/server/wwwroot/index.html +++ b/src/server/wwwroot/index.html @@ -21,10 +21,10 @@ - + - - + +