GithubHelp home page GithubHelp logo

fuzzybinary / dart-embedding-example Goto Github PK

View Code? Open in Web Editor NEW
90.0 6.0 10.0 1.8 MB

[Deprecated] An example of embedding Dart in an existing C++ application

C++ 53.37% Dart 0.04% C 46.55% PowerShell 0.04%

dart-embedding-example's Introduction

⚠ NOTE - DEPRECATED ⚠

I'm now working more on using Dart from a .dll / so, as well as building / linking from a Dart repo directly. That effort is hosted here. That repo includes more examples and scripts for building Dart directly, as well as CMAKE scripts for building for both Mac and Linux.

How to use this

Updated 07/24/2021 - Updated to Dart 2.13.4

You can clone this repo and pull out the dart directory which contains all the headers and libraries you'll need (see Other Notes about some gotchas with these libs).

DartTest2/DartTest2.cpp does all of the embedding work. Note:

  • I tried simplifying this as much as I can but there's still some complexities and code formatting differences between my style and the dart team. Sorry.
  • You can use Dart_Invoke over Dart_RunLoop to execute a single Dart function, but doing so does not drain the message queue. To do so see Draining the Message Queue
  • Debugging should work provided the service isolate starts up correctly.
  • Hot reloading works, but requires a you write your own watcher script to trigger it, as VSCode doesn't implement it for anything other than Flutter projects. see this issue for more information.
    • The Hotreloader pub package implements hot reloading for the current process, but the code can be ported for connecting to an embedded instance.
  • This does not take into account loading pre-compiled dart libraries.

Other Notes -

  • This is taken from main.cc in runtime/bin which is complicated because it supports all the various ways of booting the dart runtime in AOT, and other modes. I would like to see how to accomplish that moving forward
  • The startup time is high, mostly because of the overhead of booting the kernel and service isolates and compiling the dart.
  • The way this is currently written assumes sound null safety. There is a function Dart_DetectNullSafety that you could use instead of setting the flags->null_safety parameter directly.
  • This can now load a .dill precompiled kernel over a .dart file! Change loadDill in main to switch between using the .dart file and the .dill

How I did this

Dart doesn't have anything available that makes embedding easy. The dart.lib and header files included in the SDK are for creating extensions, not for embedding, so unfortunately, you'll have to build it yourself.

Get The Dart Source

Get the Dart SDK source according to the instructions provided at the Dart home page: https://github.com/dart-lang/sdk/wiki/Building

I most recently compiled this with Visual Studio Community 2019, but 2017 is the only "supported" version You can override the executable for building this by setting the following environment variables

set GYP_MSVS_VERSION=2017
set DEPOT_TOOLS_WIN_TOOLCHAIN=0
set GYP_MSVS_OVERRIDE_PATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\"

Make sure depot_tools is also on your path and if you have Python 3 installed on your system make sure depot_tools comes first in your path. Otherwise portions of the build that require Python 2 will fail.

Build the SDK

Just in case, let's make sure everything builds properly.

As of this writing, this just involves doing python tools/build.py --mode=release create_sdk

Modify some GN files

Dart needs a lot of extra stuff on top of the VM to get a lot of the embedding story working. Instead of trying to figure out what was necessary and not, I basically created a library that is all of dart.exe minus "main.cc" and called it "dart_lib". To do this:

  • See modifications below
  • Regenerate your ninja files for Dart (buildtools\gn.exe gen out\DebugX64)
  • Build the library
    • Move to out\DebugX64
    • ninja libdart
  • The new library will be in out\DebugX64\obj\runtime\bin
  • I copied over a bunch of header files into the dart directory locally. You could just reference them directly if you had the dart directory in your include path. You can look in the repo and see what I needed to copy other than the dart_api headers

I made other changes to GN files to suit my development style. For example, I no longer use the statically linked C runtime when I can avoid it, but dart does. If you are building this for yourself, you may need to change these settings to suit your needs.

Current .gn modifications

For simplicity, here are the current full modifications I made to the dart-sdk gn files for the libs included in this repo. These modifications are current as of the version at the top of the file.

In runtime/bin/BUILD.gn add the following:

static_library("libdart") {
  deps = [
    ":standalone_dart_io",
    "..:libdart_jit",
    "../platform:libdart_platform_jit",
    ":dart_snapshot_cc",
    ":dart_kernel_platform_cc",
    "//third_party/boringssl",
    "//third_party/zlib",
  ]
  if (dart_runtime_mode != "release") {
    deps += [ "../observatory:standalone_observatory_archive" ]
  }

  complete_static_lib = true

  if (dart_use_tcmalloc) {
    deps += [ "//third_party/tcmalloc" ]
  }

  include_dirs = [
    "..",
    "//third_party",
  ]

  sources = [
    "builtin.cc",
    "error_exit.cc",
    "error_exit.h",
    "vmservice_impl.cc",
    "vmservice_impl.h",
    "snapshot_utils.cc",
    "snapshot_utils.h",
    "gzip.cc",
    "gzip.h",
    "dfe.cc",
    "dfe.h",
    "loader.cc",
    "loader.h",
    "dart_embedder_api_impl.cc",
  ]
  if (dart_runtime_mode == "release") {
    sources += [ "observatory_assets_empty.cc" ]
  }
}

In build/config/compiler/BUILD.gn change the following (around line 424):

- # Static CRT.
+ # Dynamic CRT.
  if (is_win) {
    if (is_debug) {
-     cflags += [ "/MTd" ]
+     cflags += [ "/MDd" ]
    } else {
-     cflags += [ "/MT" ]
+     cflags += [ "/MD" ]
    }
    defines += [
      "__STD_C",
      "_CRT_RAND_S",
      "_CRT_SECURE_NO_DEPRECATE",
+     "_ITERATOR_DEBUG_LEVEL=0",
      "_HAS_EXCEPTIONS=0",
      "_SCL_SECURE_NO_DEPRECATE",
    ]

Draining the Message Queue

If you are using Dart_Invoke over Dart_RunLoop, this doesn't give dart any time to drain its message queue or perform async operations. To get this to work, you need to invoke a private method in the dart:isolate library for now. Here's the code

Dart_Handle libraryName = Dart_NewStringFromCString("dart:isolate");
Dart_Handle isolateLib = Dart_LookupLibrary(libraryName);
if (!Dart_IsError(isolateLib))
{
    Dart_Handle invokeName = Dart_NewStringFromCString("_runPendingImmediateCallback");
    Dart_Handle result = Dart_Invoke(isolateLib, invokeName, 0, nullptr);
    if (Dart_IsError(result))
    {
        // Handle error when drainging the microtask queue
    }
    result = Dart_HandleMessage();
    if (Dart_IsError(result))
    {
        // Handle error when drainging the microtask queue
    }
}

Like this?

Follow me (@fuzzybinary) on Twitter and let me know. I'd love to hear from you!

dart-embedding-example's People

Contributors

fuzzybinary avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dart-embedding-example's Issues

question about libdart

After I build , there is no libdart.a but libdart_precompiled_runtime.a.

When I link it report can not find symbol:
dart::bin::observatory_assets_archive, dart::bin::observatory_assets_archive_len, CompileAndReadScript

I want to link and run AOT binary , I guess I has to replace CompileAndReadScript part. please help to give some tips.

Could this approach be used to enable building shared/static libs for Dart, the same way that .NET does?

Heya -- thank you for maintaining these examples!

As far as I can tell, you've never been able to build shared/static libs with Dart Native. Only binaries.

Looking at the embedding example and the embedding API here, I'm curious if the approach that .NET took with DNNE (Dotnet Native Exports) would be viable?

The way this works is that you can mark functions with an attribute, to be exported (and the C99 code to generate for it if needed). At build time, it uses reflection to codegen a .h that:

  1. Initializes the .NET VM
  2. Loads in your program code
  3. Sets function pointer definitions by reading them from the VM

And then it compiles this for you automatically.

private const string ReaperPluginInfoStructString = @"
  struct reaper_plugin_info_t
  {
    int caller_version;
    void* hwnd_main;
    int (*Register)(const char* name, void* infostruct);
    void* (*GetFunc)(const char* name);
  };
";

public struct ReaperPluginInfo {} // omitted

[UnmanagedCallersOnly]
[DNNE.C99DeclCode(ReaperPluginInfoStructString)]
public static int ReaperPluginEntry(IntPtr hInstance, [DNNE.C99Type("struct reaper_plugin_info_t*")] ReaperPluginInfo* rec)
// Auto-generated by dnne-gen
// .NET Assembly: ReaperDNNE

// Declare exported functions
#ifndef __DNNE_GENERATED_HEADER_REAPERDNNE__
#define __DNNE_GENERATED_HEADER_REAPERDNNE__

#include <stddef.h>
#include <stdint.h>
#include <dnne.h>

// Additional code provided by user
      struct reaper_plugin_info_t
      {
        int caller_version;
        void* hwnd_main;
        int (*Register)(const char* name, void* infostruct);
        void* (*GetFunc)(const char* name);
      };
    

// Computed from ReaperDNNE.MyReaperPlugin.ReaperPluginEntry
DNNE_API int32_t DNNE_CALLTYPE ReaperPluginEntry(intptr_t hInstance, struct reaper_plugin_info_t* rec);

#endif // __DNNE_GENERATED_HEADER_REAPERDNNE__

// Define exported functions
#ifdef DNNE_COMPILE_AS_SOURCE

// Forward declarations
extern void* get_callable_managed_function(
    const char_t* dotnet_type,
    const char_t* dotnet_type_method,
    const char_t* dotnet_delegate_type);

extern void* get_fast_callable_managed_function(
    const char_t* dotnet_type,
    const char_t* dotnet_type_method);

// String constants
static const char_t* t1_name = DNNE_STR("ReaperDNNE.MyReaperPlugin, ReaperDNNE");

// Exports
// Computed from ReaperDNNE.MyReaperPlugin.ReaperPluginEntry
static int32_t (DNNE_CALLTYPE* ReaperPluginEntry_ptr)(intptr_t hInstance, struct reaper_plugin_info_t* rec);
DNNE_API int32_t DNNE_CALLTYPE ReaperPluginEntry(intptr_t hInstance, struct reaper_plugin_info_t* rec)
{
    if (ReaperPluginEntry_ptr == NULL)
    {
        const char_t* methodName = DNNE_STR("ReaperPluginEntry");
        ReaperPluginEntry_ptr = get_fast_callable_managed_function(t1_name, methodName);
    }
    return ReaperPluginEntry_ptr(hInstance, rec);
}

#endif // DNNE_COMPILE_AS_SOURCE

Issue when building the library using ninja

I've just been trying to follow along with the readme to get the embedding example set up but have hit a snag in the "Modifying some GN files" section of the doc. Namely, when trying to execute the ninja libdart command in the generated DebugX64 directory, I get the following error:

C:\src\depot_tools\dart-sdk\sdk\out\DebugX64>ninja libdart
ninja: error: unknown target 'libdart', did you mean 'dart'?

I'm not sure if this is caused by incorrectly generating the ninja files in previous steps or if changes have been made to the Dart SDK or depot_tools that would've changed the target I'd be attempting to build, so I was wondering if you might be able shed a little light on that.

Additionally, how do you actually run the code in this repo? Simply cloning it into Visual Studio and attempting a build throws the following error:

\dart-embedding-example\dart\platform\assert.h(12,1): fatal error C1189: #error: neither DEBUG nor NDEBUG defined

Does the source need to be placed within the depot_tools\dart-sdk directory and then built from within there? Or is it something else that I'm missing entirely?

Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.