Native Extensions

No, there is no such define. We don’t really have the concept of DEBUG/RELEASE in terms of code paths.
Instead, we base the builds on different libraries. E.g. “engine.a” vs “engine_release.a”

So, in your case, I’d suggest a “debug.appmanifest” / “release.appmanifest” approach.

1 Like

Not really understand how it can help in my case.
I want that someNE builds only when I build debug version. For now I see only one way: bob+ script + two different “game.project” file, one with link to needed NE, and another without…

I thought you wanted a define. Then you’d have to use two app manifests.

Yes, if you need to remove it completely (which the system isn’t really designed for), you’d have to have two game.project files which specify different dependencies. Note that it doesn’t work if the extension is inside the actual project.

1 Like

Ok, thanks.

1 Like

Released in 1.2.142

5 Likes

No more Wine

In the latest release (1.2.147) we finally removed support for using Wine for windows builds. We will use Clang (on Linux) going forward. This was announced in August so hopefully this isn’t a surprise to anyone.

Include paths + Base extensions

Also, a cool feature we added was the possibility to include C/C++ files from other extensions.
E.g. if you have one extension holding all the OpenSSL libraries, you can then create another extension with just the code to do what you need. This makes it easier to add several extensions that use the same “base extension”.

If you have extensions A and B, you can use one from the other by adding the extension name and include (“A/include”)to the include path. Like so

#include <A/include/a_header.h>

WIP

We’re taking tiny steps towards linking against dynamic libraries.
Step one is making the extender server support this.
Although it’s not in the sprint right now, the work has at least been started.

16 Likes

Where to include the extension name?

In the include itself. In the example above, “A” is the extension, and inside “A” is the “include” folder, so you can now include that folder.

4 Likes

image
I have an extension, named “luajavaoc”, I include test.h from another extension,but It encounters error when I build win32
test.h is inside the “include” folder in extension “luajavaoc”,like this:
image

That is indeed odd. That should have worked. Is this extension available somewhere I can access it to debug it?

I will create a new simple project a short later, and send you.

testinc.zip (81.5 KB)
This project contains two extension,exta and extb. exta has an a_func.h, which included from extb in extb.cpp

1 Like

Thanks! I’ll check it out now!

So, a clarification (or fix for my half-accurate explanation)

So the feature basically allows you to include all uploaded files. Through the “-Iupload” compile flag.
And the uploaded files will retain their relative paths (relative to the root folder of the project.
So in your case, your extension folder contains two extensions: exta and extb like so:

mathiaswesterdahl ~/work/projects/testinc  
$ tree -L 2 extension/
extension/
├── createext.sh
├── exta
│   ├── ext.manifest
│   ├── include
│   ├── lib
│   └── src
└── extb
    ├── ext.manifest
    ├── lib
    └── src

The include path needed to include a_func.h from extb then needs to be:

#include <extension/exta/include/a_func.h>

(Since I’m used to implementing extensions as standalone repositories, I’m used to seeing them on the “root level”. )

I will update the documentation with this info as well.

4 Likes

It works fine, thank you.BTW, need I explicitly set “-lupload” flag to ext.manifest?

1 Like

You’re welcome!

There’s no need to set this flag. this flag is set automatically.

1 Like

One thing we missed to communicate properly was the recent change to support iOS 12 by using the latest iPhoneOS12.1.sdk.

This SDK included changes that meant that we couldn’t use Clang 6 anymore, but instead had to revert to using Apple’s clang. And that version is <6 (It’s unclear what version they’re actually on).

The biggest change for your projects might be if you previously relied on C++11 being the default flavor, you might get build errors. Sorry about that.

The fix is then to enable C++11 for your extension:

x86_64-osx:
    context:
        flags: ["-std=c++11"]
5 Likes

Although we fairly recently added these documentation pages, it’s good to reiterate some of the best practices.

In this particular case, it’s about using std::string and knowing about the downsides to it.
Apart from added compile time, it also adds extra code bloat (as C++ templates does) and is slower than C strings.
Those are side effects you can notice yourself, but there’s another more sneaky part to it:

  • ABI compatibility

An API is basically the “names” of structs and functions:

struct Foo;
Foo* Alloc();
void DoWork(Foo*);

And ABI is the Application Binary Interface, which means how the structs are actually implemented internally.

// library version 1
struct Foo {
    uint8_t data;
};
// library version 2
struct Foo {
    uint32_t data;
};

(This is a simplified case just to illustrate the point)

If two different libraries are built towards two different versions of the ABI, and you later want to use both libraries in your executable, you can get nasty issues. At best a linker error. Or you can get a runtime error or possibly a crash (because then you at least know about it!), or at worst, a hidden bug lingering on.

In this case, we didn’t get a linker error, but sure enough, the GCC version wasn’t supported on the platform in question (SteamOS):

symbol _ZTVNSt7__cxx1119basic_istringstreamIcSt11char_traitsIcESaIcEEE
version GLIBCXX_3.4.21 not defined in file libstdc++.so.6 with link time reference

In the Defold engine itself, we stay away from the problems by simply using C strings:

#include <string.h>
#include <stdio.h>
size_t strlen(const char *s)
int snprintf(char *str, size_t size, const char *format, …)
char *strncat(char *dest, const char *src, size_t n)

So, now you know a little more about the potential issues when using STL and std:: functions
and how to avoid them.

7 Likes

Help us master! Do you have a simple code which you can share? @Mathias_Westerdahl :slight_smile:

1 Like

In it’s simplest form:

#include <dmsdk/dlib/json.h>
const char* data = ...; // the input data you've read from somewhere
dmJson::Document doc;
if (dmJson::RESULT_OK == dmJson::Parse(data, &doc)) {
    // do stuff... 
}

Internally, we use jsmn (Jasmine) and our API follows that as well.

I need to write a better example for the documentation, so this is very untested:
To traverse the DOM document:

int Traverse(dmJson::Document* doc, index) {
    if (index >= doc->m_NodeCount)
    	return -1;
    const dmJson::Node& n = doc->m_Nodes[index];
    const char* json = doc->m_Json; // the actual data
    uint32_t length = n.m_End - n.m_Start; // The range of indices that this token refers to

    switch (n.m_Type) {
    case dmJson::TYPE_PRIMITIVE:
    	if (length == 4 && memcmp(json + n.m_Start, "true", 4) == 0) {
            // ...
        }
        // more primitives
        return index + 1; // next token please!
    case dmJson::TYPE_STRING:
    	// a string ...
        return index + 1; // next token please!

    case dmJson::TYPE_ARRAY:
        ++index;
        for (int i = 0; i < n.m_Size; ++i) {
        	index = Traverse(doc, index);
            if (index < 0)
                return -1;
        }
        return index;
    case dmJson::TYPE_OBJECT: // a table
        if ((n.m_Size % 2) == 0) { // key+value
            ++index;
            for (int i = 0; i < n.m_Size; i += 2)
            {
                index = Traverse(doc, index);
                if (index < 0)
                    return -1;
                index = Traverse(doc, index);
                if (index < 0)
                    return -1;
            }
        	return index;
        }
    	// malformed
    	return -1;
    }
    return -1;
}

Edit: And this is how to use it:

if (Traverse(&doc, 0) < 0) { //index==0 is the root node
    //error
}
5 Likes