Search code examples
c++chromiumv8

How do I embed v8 on Windows?


How do you compile and embed v8 using Visual Studio on Windows? The official guide worked on Linux but not on Windows with Visual Studio because I keep getting compile errors while trying to build v8 and the sample project.


Solution

  • These instructions have been updated in 2023 for v8 version 11.5.83 on Windows 11.

    Setting up the build environment

    1. Follow the "depot_tools_tutorial" for Windows. Here is a summary of the instructions.

      1. First, download and extract depot_tools.zip.
      2. Then set your PATH variable to the folder.
      3. Create a variable named DEPOT_TOOLS_WIN_TOOLCHAIN and set it to 0.
      4. Disable App Execution Alias for python.exe and python3.exe.
      5. Open command prompt (not Powershell) and run gclient (from any folder) to download all tools.
      6. Test the setup by running the command where python. The depot_tools Python should appear first in the list. Then run python3 --version to ensure it outputs as expected.
    2. Follow "Setting up Windows".

      1. Install Visual Studio 2022 with the following components: Desktop development with C++, and MFC/ATL support.
      2. Install Windows SDK 10.0.22621.0. Download it from here and make sure to get the exact version. I first tried downloading the SDK from the Visual Studio Installer but it didn't work as it doesn't come with the required Debugging Tools.
    3. Download the v8 source code.

      1. First create a folder called v8_download, then open command prompt and cd to that folder.
      2. Run fetch v8 and then cd v8.
      3. Checkout the tag 11.5.83 with git checkout 11.5.83. I highly recommend using this tag that I tested the instructions on because future versions may change the building steps. After completing all the steps, you can try the latest commit or latest tag.
      4. Download all dependencies by running gclient sync.

    Compile v8 as a library

    1. Assuming command prompt is still in the v8_download\v8 folder, run python3 tools/dev/v8gen.py x64.debug to generate the configuration file.

    2. There was one compilation error, error C3477, that I had to fix manually. The diff looks like this. I figured that the DCHECK_EQ is just a debug check that I could comment out to fix the error.

    diff --git a/src/heap/new-spaces.cc b/src/heap/new-spaces.cc
    index 43a19fe797..ba9fff6e10 100644
    --- a/src/heap/new-spaces.cc
    +++ b/src/heap/new-spaces.cc
    @@ -1069,11 +1069,11 @@ void PagedSpaceForNewSpace::Verify(Isolate* isolate,
    
       DCHECK_EQ(current_capacity_, Page::kPageSize * CountTotalPages());
    
    -  DCHECK_EQ(
    -      AllocatedSinceLastGC() + limit() - top(),
    -      std::accumulate(begin(), end(), 0, [](size_t sum, const Page* page) {
    -        return sum + page->AllocatedLabSize();
    -      }));
    +  // DCHECK_EQ(
    +  //     AllocatedSinceLastGC() + limit() - top(),
    +  //     std::accumulate(begin(), end(), 0, [](size_t sum, const Page* page) {
    +  //       return sum + page->AllocatedLabSize();
    +  //     }));
     }
     #endif  // VERIFY_HEAP
    
    1. Replace the contents of v8\out.gn\x64.debug\args.gn with:
    is_debug = true
    target_cpu = "x64"
    v8_enable_backtrace = true
    v8_enable_slow_dchecks = true
    v8_optimized_debug = false
    v8_monolithic = true
    v8_use_external_startup_data = false
    is_component_build = false
    is_clang = false
    
    
    1. Run ninja -C out.gn/x64.debug v8_monolith.

    2. Run python3 tools/dev/v8gen.py x64.release.

    3. Replace the contents of v8\out.gn\x64.release\args.gn with:

    dcheck_always_on = false
    is_debug = false
    target_cpu = "x64"
    is_component_build = false
    v8_monolithic = true
    v8_use_external_startup_data = false
    is_component_build = false
    is_clang = false
    
    
    1. Run ninja -C out.gn/x64.release v8_monolith.

    Hello World program

    1. Create a new Console App for C++ in Visual Studio.

    2. Add the library directories to the project.

      1. Go to your project properties and change the configuration to All Configurations and Platform to All Platforms.
      2. Under VC++ Directories, add the absolute path to v8\include to Include Directories. Click Apply after this step and after each of the following steps.

      enter image description here

      1. Change Configuration to Debug and add the absolute path to v8\out.gn\x64.debug\obj to Library Directories.
      2. Repeat step 3 for the Release configuration, with the path to v8\out.gn\x64.release\obj.
      3. Change to Debug configuration. Go to C/C++ | Code Generation and change Runtime Library to Multi-threaded Debug (/MTd).
      4. Change to Release configuration. Change Runtime Library to Multi-threaded (/MT).
      5. Change to All Configurations, go to Linker | Input, and add the following files to Additional Dependencies: v8_monolith.lib;dbghelp.lib;Winmm.lib;.
      6. Go to C/C++ | Preprocessor and add V8_ENABLE_SANDBOX;V8_COMPRESS_POINTERS;_ITERATOR_DEBUG_LEVEL=0; to the Preprocessor Definitions.
    3. Replace the .cpp file contents with the following code which is v8\samples\hello-world.cc but with the include paths corrected.

    // Copyright 2015 the V8 project authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "libplatform/libplatform.h"
    #include "v8-context.h"
    #include "v8-initialization.h"
    #include "v8-isolate.h"
    #include "v8-local-handle.h"
    #include "v8-primitive.h"
    #include "v8-script.h"
    
    int main(int argc, char* argv[]) {
        // Initialize V8.
        v8::V8::InitializeICUDefaultLocation(argv[0]);
        v8::V8::InitializeExternalStartupData(argv[0]);
        std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
        v8::V8::InitializePlatform(platform.get());
        v8::V8::Initialize();
    
        // Create a new Isolate and make it the current one.
        v8::Isolate::CreateParams create_params;
        create_params.array_buffer_allocator =
            v8::ArrayBuffer::Allocator::NewDefaultAllocator();
        v8::Isolate* isolate = v8::Isolate::New(create_params);
        {
            v8::Isolate::Scope isolate_scope(isolate);
    
            // Create a stack-allocated handle scope.
            v8::HandleScope handle_scope(isolate);
    
            // Create a new context.
            v8::Local<v8::Context> context = v8::Context::New(isolate);
    
            // Enter the context for compiling and running the hello world script.
            v8::Context::Scope context_scope(context);
    
            {
                // Create a string containing the JavaScript source code.
                v8::Local<v8::String> source =
                    v8::String::NewFromUtf8Literal(isolate, "'Hello' + ', World!'");
    
                // Compile the source code.
                v8::Local<v8::Script> script =
                    v8::Script::Compile(context, source).ToLocalChecked();
    
                // Run the script to get the result.
                v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
    
                // Convert the result to an UTF8 string and print it.
                v8::String::Utf8Value utf8(isolate, result);
                printf("%s\n", *utf8);
            }
    
            {
                // Use the JavaScript API to generate a WebAssembly module.
                //
                // |bytes| contains the binary format for the following module:
                //
                //     (func (export "add") (param i32 i32) (result i32)
                //       get_local 0
                //       get_local 1
                //       i32.add)
                //
                const char csource[] = R"(
            let bytes = new Uint8Array([
              0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01,
              0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07,
              0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01,
              0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b
            ]);
            let module = new WebAssembly.Module(bytes);
            let instance = new WebAssembly.Instance(module);
            instance.exports.add(3, 4);
          )";
    
                // Create a string containing the JavaScript source code.
                v8::Local<v8::String> source =
                    v8::String::NewFromUtf8Literal(isolate, csource);
    
                // Compile the source code.
                v8::Local<v8::Script> script =
                    v8::Script::Compile(context, source).ToLocalChecked();
    
                // Run the script to get the result.
                v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
    
                // Convert the result to a uint32 and print it.
                uint32_t number = result->Uint32Value(context).ToChecked();
                printf("3 + 4 = %u\n", number);
            }
        }
    
        // Dispose the isolate and tear down V8.
        isolate->Dispose();
        v8::V8::Dispose();
        v8::V8::DisposePlatform();
        delete create_params.array_buffer_allocator;
        return 0;
    }
    
    
    1. Compile and run both Debug and Release configurations. They should output:
    Hello, World!
    3 + 4 = 7