diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..be49daba --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,169 @@ +name: 'Build' + +on: + push: + branches: + - master + tags: + - 'v*' + paths-ignore: + - '**/*.md' + - '**/*.aip' + - '.vscode/**' + - '**/*.json' + - 'setup/**' + pull_request: + branches: + - master + workflow_dispatch: + +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: ${{ github.ref_type != 'tag' }} + +env: + # Offset chosen to stay well above the last AppVeyor build counter (3.3.1251.0) at time of migration. + BUILD_VERSION_OFFSET: 2000 + DOTNET_CLI_TELEMETRY_OPTOUT: 'true' + DOTNET_NOLOGO: 'true' + SCPLIB_ENABLE_TELEMETRY: 'false' + # Disabled by default; enabled per-job below only when the VCPKG_NUGET_TOKEN secret is present. + # GITHUB_TOKEN cannot push new packages under a personal-account GitHub Packages namespace, so a + # PAT with read:packages/write:packages is required. See: + # https://github.com/microsoft/vcpkg/issues/47470 + VCPKG_BINARY_SOURCES: 'clear' + +jobs: + build: + name: 'Platform: ${{ matrix.platform }}' + runs-on: windows-2022 + permissions: + contents: read + env: + # Secrets can't be referenced directly in `if:` conditions, only in `env:`/`with:`, + # so promote it here and check env.VCPKG_NUGET_TOKEN in the step below instead. + VCPKG_NUGET_TOKEN: ${{ secrets.VCPKG_NUGET_TOKEN }} + strategy: + fail-fast: false + matrix: + platform: [x64, ARM64, x86] + outputs: + build-version: ${{ steps.version.outputs.build-version }} + steps: + - name: 'Checkout' + uses: actions/checkout@v4 + with: + submodules: recursive + lfs: true + persist-credentials: false + + - name: 'Compute build version' + id: version + shell: pwsh + run: | + $buildVersion = "3.3.$([int]$env:BUILD_VERSION_OFFSET + ${{ github.run_number }}).0" + echo "BUILD_VERSION=$buildVersion" >> $env:GITHUB_ENV + echo "build-version=$buildVersion" >> $env:GITHUB_OUTPUT + Write-Output "Building version $buildVersion" + + - name: 'Bootstrap vcpkg' + shell: cmd + run: .\XInputBridge\vcpkg\bootstrap-vcpkg.bat -disableMetrics + + - name: 'Configure vcpkg NuGet binary cache' + if: ${{ env.VCPKG_NUGET_TOKEN != '' }} + shell: pwsh + run: | + $nugetExe = & .\XInputBridge\vcpkg\vcpkg.exe fetch nuget | Select-Object -Last 1 + & $nugetExe sources add ` + -Source "https://nuget.pkg.github.com/nefarius/index.json" ` + -StorePasswordInClearText ` + -Name "GitHubPackages" ` + -UserName "nefarius" ` + -Password "$env:VCPKG_NUGET_TOKEN" + & $nugetExe setapikey "$env:VCPKG_NUGET_TOKEN" ` + -Source "https://nuget.pkg.github.com/nefarius/index.json" + echo "VCPKG_BINARY_SOURCES=clear;nuget,https://nuget.pkg.github.com/nefarius/index.json,readwrite" >> $env:GITHUB_ENV + + - name: 'Cache NuGet packages' + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'nuget.config') }} + restore-keys: | + nuget-${{ runner.os }}- + + - name: 'Install vpatch' + run: dotnet tool install --global Nefarius.Tools.Vpatch + + - name: 'Stamp version into driver and XInputBridge' + shell: cmd + run: | + vpatch --stamp-version "%BUILD_VERSION%" --target-file ".\driver\dshidmini.vcxproj" --vcxproj.inf-time-stamp + vpatch --stamp-version "%BUILD_VERSION%" --target-file ".\driver\dshidmini.rc" --resource.file-version --resource.product-version + vpatch --stamp-version "%BUILD_VERSION%" --target-file ".\XInputBridge\XInputBridge.rc" --resource.file-version --resource.product-version + + - name: 'Restore managed projects' + # msbuild's /t:Rebuild over the whole solution does not implicitly restore SDK-style + # projects; these two dotnet restore calls also transitively restore the SDK project + # referenced by both. Mirrors appveyor.yml's former before_build steps. + shell: cmd + run: | + dotnet restore .\ControlApp\ + dotnet restore .\ipctest\ + + - name: 'Build' + shell: cmd + run: .\build.cmd --target-platform ${{ matrix.platform }} + + - name: 'Publish ControlApp' + if: matrix.platform == 'x64' + shell: cmd + run: .\build.cmd PublishControlApp --skip Compile + + - name: 'Pack driver CAB' + if: matrix.platform != 'x86' + shell: cmd + run: makecab.exe /f .\DsHidMini_${{ matrix.platform }}.ddf + + - name: 'Upload artifacts' + uses: actions/upload-artifact@v4 + with: + name: dshidmini-${{ matrix.platform }} + if-no-files-found: error + path: | + disk1/*.cab + bin/**/dshidmini/*.inf + bin/**/dshidmini/*.cat + bin/**/dshidmini/*.dll + bin/**/*.pdb + bin/**/*.dll + bin/*.exe + + release: + name: 'Draft release' + needs: build + if: github.ref_type == 'tag' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: 'Download artifacts' + uses: actions/download-artifact@v4 + with: + path: artifacts + pattern: dshidmini-* + + - name: 'Create draft release' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REF_NAME: ${{ github.ref_name }} + run: | + gh release create "$REF_NAME" \ + --repo "${{ github.repository }}" \ + --title "$REF_NAME (build ${{ needs.build.outputs.build-version }})" \ + --draft \ + --generate-notes \ + artifacts/dshidmini-x64/disk1/*.cab \ + artifacts/dshidmini-ARM64/disk1/*.cab \ + artifacts/dshidmini-x64/bin/*.exe diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index f9a2c48f..0a7bbaff 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -28,7 +28,7 @@ "BuildSetup", "Clean", "Compile", - "DownloadAppVeyorArtifacts", + "DownloadCiArtifacts", "PublishControlApp", "Restore", "SignProductionBinaries" @@ -107,11 +107,11 @@ "properties": { "ArtifactsPath": { "type": "string", - "description": "Output path for DownloadAppVeyorArtifacts artifacts. Default: ./artifacts" + "description": "Output path for DownloadCiArtifacts artifacts. Default: ./artifacts" }, "BuildVersion": { "type": "string", - "description": "Build version or branch for DownloadAppVeyorArtifacts (AppVeyor artifact download)" + "description": "GitHub Actions run ID for DownloadCiArtifacts artifact download" }, "Configuration": { "type": "string", @@ -123,7 +123,7 @@ }, "NoSigning": { "type": "boolean", - "description": "Skip signing in DownloadAppVeyorArtifacts" + "description": "Skip signing in DownloadCiArtifacts" }, "SetupVersion": { "type": "string", @@ -137,9 +137,9 @@ "type": "string", "description": "Path to a solution file that is automatically loaded" }, - "Token": { + "TargetPlatform": { "type": "string", - "description": "AppVeyor API token for DownloadAppVeyorArtifacts artifact download" + "description": "Target platform for BuildDmf on CI (x64, ARM64 or x86). Not needed for local builds." } } }, diff --git a/Directory.Build.props b/Directory.Build.props index e899db2c..1fb2a48f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,6 +14,9 @@ true + + false true true snupkg diff --git a/README.md b/README.md index 31bfe3a7..631a2e56 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ # DsHidMini logoDsHidMini -[![Build status](https://ci.appveyor.com/api/projects/status/vmf09i95d06c8mbh/branch/master?svg=true)](https://ci.appveyor.com/project/nefarius/dshidmini/branch/master) [![GitHub All Releases](https://img.shields.io/github/downloads/nefarius/DsHidMini/total)](https://somsubhra.github.io/github-release-stats/?username=nefarius&repository=DsHidMini) ![GitHub issues](https://img.shields.io/github/issues/nefarius/DsHidMini) [![Discord](https://img.shields.io/discord/346756263763378176.svg)](https://discord.nefarius.at/) [![Website](https://img.shields.io/website-up-down-green-red/https/docs.nefarius.at.svg?label=docs.nefarius.at)](https://docs.nefarius.at/) +[![Build status](https://github.com/nefarius/DsHidMini/actions/workflows/build.yml/badge.svg)](https://github.com/nefarius/DsHidMini/actions/workflows/build.yml) [![GitHub All Releases](https://img.shields.io/github/downloads/nefarius/DsHidMini/total)](https://somsubhra.github.io/github-release-stats/?username=nefarius&repository=DsHidMini) ![GitHub issues](https://img.shields.io/github/issues/nefarius/DsHidMini) [![Discord](https://img.shields.io/discord/346756263763378176.svg)](https://discord.nefarius.at/) [![Website](https://img.shields.io/website-up-down-green-red/https/docs.nefarius.at.svg?label=docs.nefarius.at)](https://docs.nefarius.at/) Virtual HID Mini user-mode driver for Sony DualShock 3 controllers on Windows 10/11. @@ -84,4 +84,4 @@ Pre-built binaries and instructions: [releases](https://github.com/nefarius/DsHi - **Related:** [ScpToolkit](https://github.com/nefarius/ScpToolkit), [FireShock](https://github.com/nefarius/FireShock), [AirBender](https://github.com/nefarius/AirBender), [WireShock](https://github.com/nefarius/WireShock), [EmuController](https://github.com/FirstPlatoLV/EmuController), [USB_Host_Shield_2.0 PS3 Info](https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information#USB) - **Dependencies:** [DMF](https://github.com/microsoft/DMF), [cJSON](https://github.com/DaveGamble/cJSON), [HIDAPI](https://github.com/libusb/hidapi) - **References:** [Eleccelerator DualShock 3](http://eleccelerator.com/wiki/index.php?title=DualShock_3), [HID Usage Tables](https://usb.org/sites/default/files/documents/hut1_12v2.pdf), [The HID Page](http://janaxelson.com/hidpage.htm), [CircumSpector/Research Sony DS3](https://github.com/CircumSpector/Research/tree/master/Sony%20DualShock%203), [linux hid-sony](https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c) -- **DevOps:** [AppVeyor](https://www.appveyor.com/), [NUKE](https://nuke.build/) +- **DevOps:** [GitHub Actions](https://github.com/features/actions), [NUKE](https://nuke.build/) diff --git a/build/Build.cs b/build/Build.cs index 242e5d49..1a3dc6c5 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -1,15 +1,11 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Net.Http; -using System.Text.Json; using JetBrains.Annotations; using Nuke.Common; -using Nuke.Common.CI.AppVeyor; using Nuke.Common.Execution; using Nuke.Common.IO; using Nuke.Common.ProjectModel; @@ -27,16 +23,16 @@ class Build : NukeBuild [Solution] readonly Solution Solution; - [Parameter("Build version or branch for DownloadAppVeyorArtifacts (AppVeyor artifact download)")] - readonly string BuildVersion = ""; + [Parameter("Target platform for BuildDmf on CI (x64, ARM64 or x86). Not needed for local builds.")] + readonly string TargetPlatform = ""; - [Parameter("AppVeyor API token for DownloadAppVeyorArtifacts artifact download")] - readonly string Token = ""; + [Parameter("GitHub Actions run ID for DownloadCiArtifacts artifact download")] + readonly string BuildVersion = ""; - [Parameter("Output path for DownloadAppVeyorArtifacts artifacts. Default: ./artifacts")] + [Parameter("Output path for DownloadCiArtifacts artifacts. Default: ./artifacts")] readonly string ArtifactsPath = "./artifacts"; - [Parameter("Skip signing in DownloadAppVeyorArtifacts")] + [Parameter("Skip signing in DownloadCiArtifacts")] readonly bool NoSigning; [Parameter("Setup version for BuildSetup (e.g. 3.0.0)")] @@ -48,16 +44,18 @@ class Build : NukeBuild [NuGetPackage("Nefarius.Tools.WDKWhere", "wdkwhere.dll", Framework = "net8.0")] readonly Tool WdkWhere; - const string AppVeyorApiUrl = "https://ci.appveyor.com/api"; const string SignTimestampUrl = "http://timestamp.digicert.com"; const string SignCertName = "Nefarius Software Solutions e.U."; - AbsolutePath DmfSolution => IsLocalBuild - ? Solution.Directory / "DMF/Dmf.sln" - : "C:/projects/DMF/Dmf.sln"; + AbsolutePath DmfSolution => Solution.Directory / "DMF/Dmf.sln"; AbsolutePath ResolvedArtifactsPath => (AbsolutePath)Path.GetFullPath(Path.Combine(RootDirectory, ArtifactsPath)); + /// + /// Version stamp propagated from CI (BUILD_VERSION env var, set from github.run_number). Empty for local builds. + /// + static string BuildVersionStamp => Environment.GetEnvironmentVariable("BUILD_VERSION"); + /// /// Runs Microsoft's SignTool with the provided command-line arguments, using the explicit SignToolPath when available or delegating to the WdkWhere tool otherwise. /// @@ -112,19 +110,22 @@ void InvokeSignTool(string arguments) } else { - string appVeyorPlatform = AppVeyor.Instance?.Platform; - if (appVeyorPlatform == MSBuildTargetPlatform.x86 || - appVeyorPlatform == MSBuildTargetPlatform.Win32) + if (string.IsNullOrWhiteSpace(TargetPlatform)) + { + throw new InvalidOperationException( + "TargetPlatform must be set on CI, e.g. --target-platform x64."); + } + + if (string.Equals(TargetPlatform, "x86", StringComparison.OrdinalIgnoreCase) || + string.Equals(TargetPlatform, "Win32", StringComparison.OrdinalIgnoreCase)) { Log.Warning("DMF dropped 32-Bit support, skipping build"); return; } - MSBuildTargetPlatform platform = appVeyorPlatform switch - { - "ARM64" => "ARM64", - _ => MSBuildTargetPlatform.x64 - }; + MSBuildTargetPlatform platform = string.Equals(TargetPlatform, "ARM64", StringComparison.OrdinalIgnoreCase) + ? (MSBuildTargetPlatform)"ARM64" + : MSBuildTargetPlatform.x64; buildCombinations = [(Configuration, platform)]; } @@ -159,6 +160,22 @@ void InvokeSignTool(string arguments) .SetNodeReuse(IsLocalBuild) .SetVerbosity(MSBuildVerbosity.Minimal); + // On CI, MSBuild must be told the solution platform explicitly. AppVeyor's "platform:" matrix + // axis set a $env:PLATFORM variable that MSBuild picks up implicitly for the unset Platform + // property; GitHub Actions has no such variable, so without this MSBuild falls back to + // "Any CPU", which dshidmini.sln maps to a Release|x64 build of the driver project for every + // leg (including x86), pulling in a DMF x64 lib that BuildDmf never produced for that leg. + if (!IsLocalBuild) + { + if (string.IsNullOrWhiteSpace(TargetPlatform)) + { + throw new InvalidOperationException( + "TargetPlatform must be set on CI, e.g. --target-platform x64."); + } + + settings = settings.SetTargetPlatform((MSBuildTargetPlatform)TargetPlatform); + } + // Aggressively silence C# warnings for local Nuke builds (nullability, CS8981, XML docs, etc.) if (IsLocalBuild) { @@ -167,6 +184,17 @@ void InvokeSignTool(string arguments) settings = settings.SetProperty("NoWarn", noWarn.Replace(";", "%3B")); } + // Stamps managed projects (ControlApp, SDK, ipctest, installer) with the CI build version, + // replacing AppVeyor's dotnet_csproj auto-patching. C++ projects ignore unknown properties. + if (!string.IsNullOrWhiteSpace(BuildVersionStamp)) + { + settings = settings + .SetProperty("Version", BuildVersionStamp) + .SetProperty("AssemblyVersion", BuildVersionStamp) + .SetProperty("FileVersion", BuildVersionStamp) + .SetProperty("InformationalVersion", BuildVersionStamp); + } + return settings; }); }); @@ -194,6 +222,15 @@ void InvokeSignTool(string arguments) "CS0219;CS1587;CS1591;CS8600;CS8601;CS8602;CS8603;CS8604;CS8618;CS8619;CS8622;CS8625;CS8629;CS8765;CS8767;CS8981"; s = s.SetProperty("NoWarn", noWarn.Replace(";", "%3B")); + if (!string.IsNullOrWhiteSpace(BuildVersionStamp)) + { + s = s + .SetProperty("Version", BuildVersionStamp) + .SetProperty("AssemblyVersion", BuildVersionStamp) + .SetProperty("FileVersion", BuildVersionStamp) + .SetProperty("InformationalVersion", BuildVersionStamp); + } + return s .SetProject(controlAppProjectPath) .SetConfiguration(Configuration.Release) @@ -208,120 +245,43 @@ void InvokeSignTool(string arguments) }); /// - /// Download AppVeyor build artifacts (ARM64, x64, x86) and optionally sign CABs, EXEs, and driver/XInput DLLs. - /// Requires BuildVersion and Token. Use --NoSigning to skip signing. + /// Download GitHub Actions build artifacts (ARM64, x64, x86) for a tagged run and optionally sign CABs, EXEs, + /// and driver/XInput DLLs. Requires BuildVersion (a GitHub Actions run ID) and the "gh" CLI to be authenticated + /// (run "gh auth login" once). Use --NoSigning to skip signing. /// [UsedImplicitly] - public Target DownloadAppVeyorArtifacts => _ => _ + public Target DownloadCiArtifacts => _ => _ .Executes(() => { - if (string.IsNullOrWhiteSpace(BuildVersion) || string.IsNullOrWhiteSpace(Token)) + if (string.IsNullOrWhiteSpace(BuildVersion)) { - throw new InvalidOperationException("DownloadAppVeyorArtifacts requires BuildVersion and Token."); + throw new InvalidOperationException( + "DownloadCiArtifacts requires BuildVersion (a GitHub Actions run ID, see the \"Build\" workflow run URL)."); } string artifactsDir = ResolvedArtifactsPath; Directory.CreateDirectory(artifactsDir); - using HttpClient http = new(); - http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token); - - string projectUri = $"{AppVeyorApiUrl}/projects/nefarius/DsHidMini/build/{Uri.EscapeDataString(BuildVersion)}"; - Log.Information("Fetching build info: {Uri}", projectUri); - string json = http.GetStringAsync(projectUri).GetAwaiter().GetResult(); - using (JsonDocument buildDoc = JsonDocument.Parse(json)) - { - JsonElement build = buildDoc.RootElement.GetProperty("build"); - JsonElement jobs = build.GetProperty("jobs"); - - string[] jobNames = ["Platform: ARM64", "Platform: x64", "Platform: x86"]; - foreach (string jobName in jobNames) - { - string? jobId = null; - foreach (JsonElement job in jobs.EnumerateArray()) - { - if (job.GetProperty("name").GetString() != jobName) - continue; - JsonElement jobIdEl = job.GetProperty("jobId"); - jobId = jobIdEl.ValueKind == JsonValueKind.String - ? jobIdEl.GetString()! - : jobIdEl.GetInt32().ToString(); - break; - } - - if (string.IsNullOrEmpty(jobId)) - { - Log.Warning("Job not found: {JobName}", jobName); - continue; - } - - string artifactsListUri = $"{AppVeyorApiUrl}/buildjobs/{jobId}/artifacts"; - string listJson = http.GetStringAsync(artifactsListUri).GetAwaiter().GetResult(); - using JsonDocument listDoc = JsonDocument.Parse(listJson); - string artifactsDirFull = Path.GetFullPath(artifactsDir); - string artifactsDirRoot = artifactsDirFull.TrimEnd(Path.DirectorySeparatorChar); - - foreach (JsonElement artifact in listDoc.RootElement.EnumerateArray()) - { - string fileName = artifact.GetProperty("fileName").GetString()!; - string fileNameDecoded = Uri.UnescapeDataString(fileName); - string fileNameNormalized = fileNameDecoded.Replace('/', Path.DirectorySeparatorChar); - string candidatePath = Path.Combine(artifactsDirFull, fileNameNormalized); - string fullPath = Path.GetFullPath(candidatePath); - - if (!fullPath.StartsWith(artifactsDirRoot + Path.DirectorySeparatorChar, StringComparison.Ordinal) - && fullPath != artifactsDirRoot) - { - Log.Error("Path traversal blocked: artifact fileName \"{FileName}\" resolves outside {ArtifactsDir}", - fileName, artifactsDirFull); - continue; - } - - string fileNameEncoded = Uri.EscapeDataString(fileName); - string downloadUri = - $"{AppVeyorApiUrl}/buildjobs/{jobId}/artifacts/{fileNameEncoded}"; - Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!); - byte[] bytes = http.GetByteArrayAsync(downloadUri).GetAwaiter().GetResult(); - File.WriteAllBytes(fullPath, bytes); - Log.Information("Downloaded {File}", fullPath); - } - } - } + ProcessTasks.StartProcess("gh", + $"run download {BuildVersion} --repo nefarius/DsHidMini --dir \"{artifactsDir}\" --pattern \"dshidmini-*\"") + .AssertZeroExitCode(); if (!NoSigning) { - string[] files = - [ - Path.Combine(artifactsDir, "disk1", "*.cab"), Path.Combine(artifactsDir, "bin", "*.exe"), - Path.Combine(artifactsDir, "bin", "ARM64", "dshidmini", "dshidmini.dll"), - Path.Combine(artifactsDir, "bin", "x64", "dshidmini", "dshidmini.dll"), - Path.Combine(artifactsDir, "bin", "x64", "XInput1_3.dll"), - Path.Combine(artifactsDir, "bin", "ARM64", "XInput1_3.dll"), - Path.Combine(artifactsDir, "bin", "x86", "XInput1_3.dll") - ]; - List existingFiles = new(); - foreach (string pattern in files) - { - string dir = Path.GetDirectoryName(pattern)!; - string search = Path.GetFileName(pattern); - if (search.Contains('*')) - { - if (Directory.Exists(dir)) - { - existingFiles.AddRange(Directory.GetFiles(dir, search)); - } - } - else if (File.Exists(pattern)) - { - existingFiles.Add(pattern); - } - } - + string[] patterns = ["*.cab", "*.exe", "dshidmini.dll", "XInput1_3.dll"]; + List existingFiles = patterns + .SelectMany(pattern => Directory.GetFiles(artifactsDir, pattern, SearchOption.AllDirectories)) + .ToList(); + if (existingFiles.Count > 0) { InvokeSignTool( $"sign /v /n \"{SignCertName}\" /tr {SignTimestampUrl} /fd sha256 /td sha256 {string.Join(" ", existingFiles.Select(f => $"\"{f}\""))}"); } + else + { + Log.Warning("No files found to sign under {ArtifactsDir}", artifactsDir); + } } Log.Information("Helper job names for sign portal:"); diff --git a/setup/README.md b/setup/README.md index 53da81b5..d8798cb2 100644 --- a/setup/README.md +++ b/setup/README.md @@ -6,12 +6,13 @@ This project generates an MSI package containing x64 and ARM64 driver editions u Commands/scripts are to be run from solution root directory. -- Tag a release and let it build on CI +- Tag a release and let it build on CI (the "Build" GitHub Actions workflow) +- Authenticate the GitHub CLI once with `gh auth login` (needs `repo` scope) - Use ```PowerShell - nuke download-appveyor-artifacts -token $appVeyorToken -buildversion "3.3.1251.0" + nuke download-ci-artifacts -buildversion "" ``` - to download the tagged release + to download the tagged release (the run ID is the numeric ID in the workflow run URL) - Submit the `*.cab` files to MS Partner Portal for signing - Place the signed files in `.\artifacts\drivers` directory - Run