Selam Dostlar…

C# Experimental Serimizin İlk Yazısında C# Native Kütüphane Derleyeceğiz!?

Peki Nasıl Oluyor Bu? Hani C# Native Dil Değildi .Net Core’nin Bile Boyutları Uçuk Kaçık Oluyordu Runtime Bağımsız Derlediğimizde…

Geçenlerde .NET’in Githubdaki Geliştirme Deposunda Rastladım. NativeAOT, NativeAOT-LLVM , NativeAOT-Interpreter Adı 3 Branş Oluşturmuşlar. Biraz Araştırayım dedim ve Anladımki Microsoft Gelecek Yıl .NET 6 için LLVM Interpreter ve Aot Desteği Getirmeyi Planlıyor.

Öncelikle Nedir Bu Kavramlar Bundan Biraz Bahsedelim.

AOT Namıdiğer Ahead-Of-Time Derleyicileri Nedir?

AOT Derleyicileri Java yada Python Tarzı Ara Katman Seviyelerde Kullanan Yerel Derleme için Kullanılan Derleyicilerdir.

Örnek Java Kodumuzu JAR Şeklinde Bırakmak Yerine Baştan Aşağı Makine Diline Derleyerek Hem Hızlı Hem Küçük Boyutlu İşletim Sistemine Göre Derlenmiş Dosya Çıkartır Örnek Windowsta Exe Linuxta Elf Gibi Bu Dosyalar Küçük Boyutlu Çok Hızlı ve Güvenli Olurlar Genelde Interpreter yada Runtime Bağımlılığından Kurtulmak için Aot Derleyicileri Kullanılır.

Peki AOT’un C# ile Ne Alakası Var Nerden Buraya Geldik Derseniz İşte Bahsettiğim Geliştirme Deposunda Microsoft Bu Güzel AOT Derleyicilerini .NET Platformuna Tamamen Entegre Etmeyi Planlıyor. Zaten Şuanda Xamarin Platformunda Android ve iOS için AOT Desteği Bulunuyor Ancak .Net’in Diğer Kısımları JIT yani Native ile Desteklenen Derleme Kullanıyor.

.NET 6’da Umuyoruzki Release Şeklini Projelerimizde Görürürüz.

LLVM Nedir? (Low-Level Virtual Machine)

LLVM yani Low-Level Virtual Machine C++ ile Yazılmış bir Derleyici Alt Yapısıdır.

%95 Oranında C/C++ Kodlarını Optimize Etmek ve Native Koda Dönüştürmek %5 ise Diğer LLVM Destekli Dilleri Native Koda Dönüştürmek Kullanılır.

LLVM CLang GCC veya Başka Bir Derleyicilerin Derlediği Dosyaları Native Koda Çevirir ve Optimize Edip Size İstediğiniz İşlemci Mimarisi ve İşletim Sisteminde İkili Dosya(Binary) Verir.

Örnek Verirsek GCC ile Derlediğimiz C Kodunu LLVM’e Verdiğimizde Bize Daha Sağlam Hızlı Çalışan Native Binary Dosya Çıkartır.

Yıllardır C#‘de Sadece Native Kitaplık Import Edip Onu Kullanabilirken Artık .NET 6’da C# ile Küçük Boyutlu Native Kitaplıklar Derleyebileceğiz.

Şuanda C# de Dll ile Kodu Export Edebilsekte Native Kitaplık Olmadığından ve Sadece C# de Kullanabildiğinden Diğer Dillerde Bir İşimize Yaramıyordu.

Şimdi İse Interpreter Nedir Bundan Bahsedelim ve Uygulamalı Kısma Geçelim.

Interpreter Diller Nedir? (Yorumlanan Diller)

Interpreter Yani Yorumlayıcı Bir Dilin Kodlarını Satır Satır Yürütür ve Derleme Gerekmez.

Örnek Verecek Olursak Pythonda Komut Satırına python Yazıp Enterlediğimizde Bize Pythonun Yorumlayıcısı Yani Interpreter’i Açılır Burda Aynı Cmd Kullanır Gibi Kod Yazarak Program Oluşturabiliriz.

Ancak Interpreter Diller Çoğunlukla Derlenmez ve Text Dosyasında Kalırlar yani Herkes Okuyabilir.

Çift Tıkla Açıldığında Yorumlayıcı Otomatik Olarak Yorumlar ve Program Çalışır.

Evet! Kavramlarımızdan Bahsettiğimize Göre Artık Başlayabiliriz.

LLVM ve MSVC Altyapısı Kullanarak C# Kodunu Native Kitaplığa Çeviriyoruz!

Bu İşlemleri Yapmak için Gereksinimler:

  • Visual Studio 2019 veya Visual Studio Code
  • .NET 5.0
  • Dotnet Experimental NuGet Deposu
  • Native Linux Derleme için: Windows Subsystem for Linux veya Dotnet-Sdk-5.0 ve Clang/Gcc/Llvm Yüklü Herhangi Bir Linux İşletim Sistemi.
  • Native Windows Derleme için: Visual C++ MSVC Derleyicileri
  • Native MacOS Derleme için: Bilmem Kaç Gblik XCode (Diğer Bağımlılıklardan Bahsetmiyorum Bile Yazının En Sonunda Vereceğim Linkten .NET’in Geliştirme Deposunda Bağımlılıklara Bakabilirsiniz)

Bu Gereklilikleriniz Hazırsa Linux Kitaplık Derlemesi ile Başlayalım.

Öncelikle Visual Studio 2019’dan Bir Sınıf Kitaplığı(Class Library) Oluşturuyoruz.

Visual Studio Code Kullanıyorsanız Komut Satırı Üzerinden dotnet Komutu ile Class Library Oluşturabilirsiniz.

Visual Studio 2019 Class Library Oluşturma

Proje Adını Belirledikten Sonra Hedef Çerçeve Seçme Ekranına Geliyoruz.

Burda Arkadaşlar Kesinlikle Ama Kesinlikle Dikkat Etmemiz Gereken Husus Hedef Çerçeve Olarak .NET5 Seçiyoruz Diğer Platformlarla Native Kitaplık Derleyemezsiniz Eğer Göremiyorsanız Muhtemelen .NET5 Yüklü Değildir.

Hedef Çerçeve Seçimi

Projemizi Oluşturduk ve Şimdi ise Dikkat Etmemiz Gereken 2.Husus ise .NET’in Nuget Geliştirme Deposuna İhtiyacımız Var.

Bunun için Nuget Paketlerini Yönettiğimiz Kısma Giriyoruz. (VsCode Kullananlar Muhtemelen Terminalden dotnet Komutu ile Nasıl Nuget Paketi ve Depo Ekleneceğini Biliyordur Bilmiyorsanız Araştırmasını Google Üzerinden Yapabilirsiniz)

Burdan Okla Gösterilen Kısımdaki Ayarlara Giriyoruz ve Bize Nugetin Paket Kaynakları Kısmı Geliyor.

Buradan dotNet Experimental Deposunu Verdiğim Url ile Ekliyoruz.

https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json

Güncelleştir Butonuna Basarak Depoyu Eklemiş Olduk.

Şimdi ise Nugete Geri Dönüp Experimental Deposuna Geçiyoruz.

Paket Kaynağı Experimental Deposuna verdiğimiz Ad Olan Depo Olacak.

Gözat Kısmına Geçip Ön İzleme Sürümlerini Dahil Et Tikini İşaretleyip Microsoft.DotNet.ILCompiler.LLVM Paketini Aratıyoruz.

Karşımıza Çıkan Sonuçlar Bu Şekilde Oluyor Derlemek İstediğiniz Platforma Göre Runtimeleri Yükleyip En Baştaki Kütüphaneyi Yüklüyoruz. Bu Yazıda Windows vs Linux için Derleme Yapacağım için osx-x64 Runtimesi Dışındaki Tüm Ekranda Gözüken Paketleri Yüklüyorum.

Şimdi Kod Kısmına Geçebiliriz ve using ile Kütüphaneyi Import Ediyoruz.

using System.Runtime.InteropServices;

Native Kitaplığı Kullanan Dillerin Görebileceği Bir Fonksiyon Oluşturmak için Static Fonksiyon Açıyoruz. Ben Topla(int sayi1,int sayi2) Adında Bir Static Fonksiyon Açıyorum ve Toplama İşlemini Yaptırıp Return Ediyorum.

Fonksiyonumuzu Oluşturduktan Sonra Burda Çok Önemli Bir Kısım Var Fonksiyonu Diğer Dillerin Görebilmesi için UnmanagedCallersOnly Attributesini Fonksiyonumuza Atamamız Gerekiyor Entrypoint yani Diğer Dillerde Gözükecek Fonksyion İsmini Yazarak Atamayı Yapıyoruz.

Kodumuz Bu Şekilde Oluyor:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
using System;
using System.Runtime.InteropServices;
namespace NativeDerleme
{
    public class Class1
    {
        [UnmanagedCallersOnly(EntryPoint = "topla")]
        public static int Topla(int sayi1,int sayi2)
        {
            return sayi1 + sayi2;
        }
    }
}

Not!: LLVM ile Native Derleme Projesi Daha Çok Yeni Olduğu için Çoğu Türü Direkt Olarak Return Edemiyor veya Alamıyorsunuz int,byte,char ve Birkaç Tür Dışındaki Tüm Diğer Türleri IntPtr Türünden Çevirmek Zorundasınız ve Bu Biraz Sıkıntılı Olabiliyor Zorlanmamak için Bu Yazıda IntPtr to String Olayını Göstermeyeceğim.

Artık Projemiz Hazır ve Linux Derleme için WSL Terminalini Projenin Olduğu Klasörde Açalım.

WSL Ubuntu Terminalini Açtıktan Sonra Apt ile Gerekli Bağımlılıkları Yüklüyoruz. (dotnet-sdk 5.0 Yüklemeyi Anlatmıyorum. Microsoftun Sitesinden Depoyu Ekleyip Apt ile Kurabilirsiniz)

sudo apt-get install -y cmake llvm-9 clang-9 autoconf automake \
libtool build-essential python curl git lldb-6.0 liblldb-6.0-dev \
libunwind8 libunwind8-dev gettext libicu-dev liblttng-ust-dev \
libssl-dev libnuma-dev libkrb5-dev zlib1g-dev ninja-build

Bu Paketleri Yükledikten Sonra Projemizin Ana Klasörüne Geliyoruz.

WSL Terminalindeki dotnet ile Visual Studionun Kullandığı dotnet Apayrı Oldukları için Publish Etmeye Kalktığımızda Bize Experimental Diye Repo Olmadığını ILCompiler Paketlerini Bulamadığını Söylecek Bunun için Bir Tane nuget.config Adlı Dosya Oluşturuyoruz.

Dosya Listesi

Dosyanın İçeriğine Bunları Yapıştırıyoruz ve Kaydediyoruz:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <packageSources>
  
    <clear />
    <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
 </packageSources>
</configuration>

Bağımlılıklarımızda Halletiğimize Göre Derleme İşlemine Geçebiliriz.

WSL Ubuntu Terminalinde Projemizin Ana Klasörüne Geliyoruz.

Static (.a) Bir Kütüphane Derlemek Bu Komutu:

dotnet publish /p:NativeLib=Static -r linux-x64 -c release

Shared Yani Paylaşımlı Kütüphane (.so) Derlemek için Bu Komutu Kullanıyoruz.

dotnet publish /p:NativeLib=Shared -r linux-x64 -c release

Static Kitaplık Boyutu Biraz Büyük Olduğundan Shared Yani .so Kitaplığı Derliyoruz.

Verdiğim Komutu Yazıp Enterledikten Sonra Bizi Biraz Bu Ekranda Bekletiyor:

Bize Kodun Native Koda Dönüştürüldüğünü Söylüyor.

İşte Başardık ? Bize Belirttiği Klasörde Artık Bir Shared Linux Kütüphanemiz Var.

Dosya 12Mb Biraz Fazla Büyük Değilmi? Hemen Strip Komutunu Kullanarak Dosyamızın Boyutunu Düşürüyoruz.

Gördüğünüz Üzere Yarı Yarıya Boyutu Düşürdük ve 6Mb lik Bir Kütüphane Elde Ettik Şimdi ise Hızlıca Windows Derlememizi Yapalım ve Bunları Başka Bir Dilde Kullanalım.

Windows Derleme için WSL Terminalinden Çıkıyoruz ve Projemizin Yine Ana Klasörüne Dönüyoruz.

Bu Sefer Aynı Komutların RID Kısmını win-x64 Olarak Değiştiriyoruz Windowsunuzda MSVC Visual C++ Derleyicileri Yüklüyse Sıkıntı Yapmadan Derleme Yapacaktır.

Static Lib Derleme:

dotnet publish /p:NativeLib=Static -r win-x64 -c release

Shared Lib Derleme:

dotnet publish /p:NativeLib=Shared -r win-x64 -c release

Hemen Shared Derlememizi Yapalım Windows için.

Windows için Kütüphane Oluşturmayıda Tamamladık ve Şimdi Bunları Bir Klasöre Toplayalım ve Python Üzerinde Test Edelim.

Bu Şekilde Klasörlere Topladık Kitaplıklarımızı ve Linux için Ayrı Windows için Ayrı Python Test Dosyaları Oluşturuyoruz ve Aşağıda Verdiğim içeriği Yapıştırıyoruz.

Windows Test Kodu:

import ctypes

kutuphanemiz = ctypes.CDLL("./NativeDerleme.dll")

print(str(kutuphanemiz.topla(4,5)))

Linux Test Kodu:

import ctypes

kutuphanemiz = ctypes.CDLL("./NativeDerleme.so")

print(str(kutuphanemiz.topla(4,5)))

Bir Tane Terminal Açalım ve Önce Windows Testini Yapalım.

Gördüğünüz Gibi Direkt Olarak Toplamayı Yaptı ve Çıktı Gerçekten .NET 6’da Eğer Gelirse Süper Bir Özellik Olacak.

Şimdi ise Wsl Terminaline Geçelim ve Linux Testini Yapıp file ile Dosya Bilgilerine Bakalım.

Linuxtada Sorunsuz Şekilde Sonucu Verdi ve Çıktı.

File ile Dosya Bilgilerine Baktığımızda Linux Kitaplığının Dynamic Olarak Derlenmiş .SO Olduğunu Açıkça Anlayabiliyoruz.

Dll Olan Kitaplık PE32 Dll 64bit Olarak Gözüküyor.

Bu Kitaplıkları Import Destekleyen İstediğimiz Dilde Kullanabiliriz.

Sıkıntısız Runtime Bağımlılığı Olmadan Çok Hızlı Şekilde Çalışacaktır.

Yazımızın Sonuna Geldik Dostlar Şimdilik Daha Geliştirme Aşamasında Olduğu için Bahsettiğim Gibi Özellik Yok Pek Fazla Ancak .NET 6’da Gelirse Çok iyi Bir Gelişme Olacağını ve Artık Cross Platformda Daha Rahat Derleme Yapılabileceğini Düşünüyorum.

Okuyucularıma Teşekkür Ederim.


Linkler:

Codeksion - Telegram

.NET RuntimeLab Geliştirme Deposu

- NativeAOT

- NativeAOT-LLVM

- NativeAOT-Interpreter

- LLVM Gereksinimleri

- LLVM Resmi Örnek Uygulama