Code Monkey home page Code Monkey logo

Comments (14)

KennethL avatar KennethL commented on August 29, 2024 2

I would prefer the use of rebar3 in this project, definitely not mix since that creates an unnecessary dependency to Elixir. It should be trivial to use rebar3 for this. Using rebar3 would also get reid of the dependency to 7z that came up when I tried to 'make escript'.

from gradualizer.

gomoripeti avatar gomoripeti commented on August 29, 2024 1

hello all,

I also hit this problem, and renaming the parent dir was the ultimate solution. Strangely enough if I used zip and not 7z to create the escript (ESCRIPT_ZIP env var) it worked so I thought initially it's an OS X/7z issue.

Anyway I just introduced erlang.mk because the project already used custom make rules, and I've never used erlang.mk before, so it was a good chance to try - rather subjective choice. So I have no objection in converting to rebar3.

(To be fair you can look at the erlang.mk file is just a blackbox library, peeking into rebar3 itself could be just as hairy)

from gradualizer.

josefs avatar josefs commented on August 29, 2024 1

It sounds like we have a consensus to start to use rebar3 and I don't have any objection to that. Is anyone willing to submit a PR?

from gradualizer.

gomoripeti avatar gomoripeti commented on August 29, 2024 1

Actually as Gradualizer has no dependencies it should already work perfectly with rebar3 too (without a rebar.config).

So if you are on windows and you don't have make, but you have rebar3, you can just use it

rebar3 compile
rebar3 escriptize
rebar3 eunit

Also this is why Gradualizer works perfectly as a rebar3 dependency/plugin.

But I am absolutely not against converting to rebar3 "officially", this mostly consists of updating the README (and removing the huge erlang.mk file)

One con of erlang.mk is that I dont think it supports creating a hex package.

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

Perhaps switching to rebar3 (or even Elixir's mix, which works quite well for erlang projects as well) would be better?

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

Found a verbosity switch:

╰─➤  V=2 make escript-zip
erlang.mk:30: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
set -x; rm -f /home/username/tools/Gradualizer/.erlang.mk/deps.log
+ rm -f /home/username/tools/Gradualizer/.erlang.mk/deps.log
set -x; rm -f /home/username/tools/Gradualizer/.erlang.mk/apps.log
+ rm -f /home/username/tools/Gradualizer/.erlang.mk/apps.log
set -x; mkdir -p /home/username/tools/Gradualizer/.erlang.mk
+ mkdir -p /home/username/tools/Gradualizer/.erlang.mk
set -x; set -e; for dep in  ; do \
        mkdir -p $dep/ebin; \
done
+ set -e
set -x; set -e; for dep in  ; do \
        if grep -qs ^$dep$ /home/username/tools/Gradualizer/.erlang.mk/apps.log; then \
                :; \
        else \
                echo $dep >> /home/username/tools/Gradualizer/.erlang.mk/apps.log; \
                make -C $dep IS_APP=1; \
        fi \
done
+ set -e
set -x; mkdir -p /home/username/tools/Gradualizer/.erlang.mk
+ mkdir -p /home/username/tools/Gradualizer/.erlang.mk
set -x; set -e; for dep in  ; do \
        if grep -qs ^$dep$ /home/username/tools/Gradualizer/.erlang.mk/deps.log; then \
                :; \
        else \
                echo $dep >> /home/username/tools/Gradualizer/.erlang.mk/deps.log; \
                if [ -f $dep/GNUmakefile ] || [ -f $dep/makefile ] || [ -f $dep/Makefile ]; then \
                        make -C $dep IS_DEP=1; \
                else \
                        echo "Error: No Makefile to build dependency $dep." >&2; \
                        exit 2; \
                fi \
        fi \
done
+ set -e
set -x; erl +A0 -noinput -boot start_clean  -pz /home/username/tools/Gradualizer/.erlang.mk/rebar/ebin -eval "        E = ets:new(makedep, [bag]),  G = digraph:new([acyclic]),     ErlFiles = lists:usort(string:tokens(\"src/absform.erl src/constraints.erl src/gradualizer.erl src/gradualizer_cli.erl src/gradualizer_db.erl src/gradualizer_file_utils.erl src/gradualizer_lib.erl src/rebar_prv_gradualizer.erl src/ty.erl src/typechecker.erl src/typelib.erl  \", \" \")),     DepsDir = \"/home/username/tools/Gradualizer/deps\",  AppsDir = \"/home/username/tools/Gradualizer/apps\",        DepsDirsSrc = \"\",     DepsDirsInc = \"\",     AppsDirsSrc = \"\",     AppsDirsInc = \"\",   DepsDirs = lists:usort(string:tokens(DepsDirsSrc++DepsDirsInc, \" \")), AppsDirs = lists:usort(string:tokens(AppsDirsSrc++AppsDirsInc, \" \")),       Modules = [{list_to_atom(filename:basename(F, \".erl\")), F} || F <- ErlFiles], Add = fun (Mod, Dep) ->               case lists:keyfind(Dep, 1, Modules) of                  false -> ok;                    {_, DepFile} ->              {_, ModFile} = lists:keyfind(Mod, 1, Modules),                           ets:insert(E, {ModFile, DepFile}),                           digraph:add_vertex(G, Mod),                              digraph:add_vertex(G, Dep),                             digraph:add_edge(G, Mod, Dep)         end     end,    AddHd = fun (F, Mod, DepFile) ->                case file:open(DepFile, [read]) of                   {error, enoent} ->                               ok;                     {ok, Fd} ->                             {_, ModFile} = lists:keyfind(Mod, 1, Modules),                                case ets:match(E, {ModFile, DepFile}) of                                     [] ->                                            ets:insert(E, {ModFile, DepFile}),                                              F(F, Fd, Mod,0);                                      _ -> ok                         end             end     end,    SearchHrl = fun      F(_Hrl, []) -> {error,enoent};           F(Hrl, [Dir|Dirs]) ->                   HrlF = filename:join([Dir,Hrl]),                     case filelib:is_file(HrlF) of                            true  ->                                {ok, HrlF};                          false -> F(Hrl,Dirs)                     end     end,    Attr = fun              (_F, Mod, behavior, Dep) ->                     Add(Mod, Dep);                (_F, Mod, behaviour, Dep) ->                    Add(Mod, Dep);          (_F, Mod, compile, {parse_transform, Dep}) ->                 Add(Mod, Dep);          (_F, Mod, compile, Opts) when is_list(Opts) ->                  case proplists:get_value(parse_transform, Opts) of                            undefined -> ok;                                Dep -> Add(Mod, Dep)         end;             (F, Mod, include, Hrl) ->                       case SearchHrl(Hrl, [\"src\", \"include\",AppsDir,DepsDir]++AppsDirs++DepsDirs) of                            {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl);                              {error, _} -> false  end;             (F, Mod, include_lib, Hrl) ->                   case SearchHrl(Hrl, [\"src\", \"include\",AppsDir,DepsDir]++AppsDirs++DepsDirs) of                            {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl);                              {error, _} -> false  end;             (F, Mod, import, {Imp, _}) ->                   IsFile =                                case lists:keyfind(Imp, 1, Modules) of                                        false -> false;                                 {_, FilePath} -> filelib:is_file(FilePath)                            end,                    case IsFile of                          false -> ok;                         true -> Add(Mod, Imp)                    end;            (_, _, _, _) -> ok      end,    MakeDepend = fun                (F, Fd, Mod, StartLocation) ->                        {ok, Filename} = file:pid2name(Fd),                     case io:parse_erl_form(Fd, undefined, StartLocation) of                               {ok, AbsData, EndLocation} ->                                   case AbsData of      {attribute, _, Key, Value} ->                                                    Attr(F, Mod, Key, Value),                            F(F, Fd, Mod, EndLocation);                                              _ -> F(F, Fd, Mod, EndLocation)                              end;                             {eof, _ } -> file:close(Fd);                            {error, ErrorDescription } ->                file:close(Fd);                          {error, ErrorInfo, ErrorLocation} ->                                    F(F, Fd, Mod, ErrorLocation)                  end,                    ok      end,    [begin          Mod = list_to_atom(filename:basename(F, \".erl\")),  {ok, Fd} = file:open(F, [read]),         MakeDepend(MakeDepend, Fd, Mod,0)       end || F <- ErlFiles],  Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),   CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],       TargetPath = fun(Target) ->             case lists:keyfind(Target, 1, Modules) of                    false -> \"\";                   {_, DepFile} ->                         DirSubname = tl(string:tokens(filename:dirname(DepFile), \"/\")),                             string:join(DirSubname ++ [atom_to_list(Target)], \"/\")                end     end,    ok = file:write_file(\"gradualizer.d\", [             [[F, \"::\", [[\" \", D] || D <- Deps], \"; @touch \$@\n\"] || {F, Deps} <- Depend],         \"\nCOMPILE_FIRST +=\", [[\" \", TargetPath(CF)] || CF <- CompileFirst], \"\n\"  ]),     halt()" -- erlang.mk
+ erl +A0 -noinput -boot start_clean -pz /home/username/tools/Gradualizer/.erlang.mk/rebar/ebin -eval '       E = ets:new(makedep, [bag]),  G = digraph:new([acyclic]),     ErlFiles = lists:usort(string:tokens("src/absform.erl src/constraints.erl src/gradualizer.erl src/gradualizer_cli.erl src/gradualizer_db.erl src/gradualizer_file_utils.erl src/gradualizer_lib.erl src/rebar_prv_gradualizer.erl src/ty.erl src/typechecker.erl src/typelib.erl  ", " ")), DepsDir = "/home/username/tools/Gradualizer/deps",    AppsDir = "/home/username/tools/Gradualizer/apps",  DepsDirsSrc = "",       DepsDirsInc = "",       AppsDirsSrc = "",       AppsDirsInc = "",       DepsDirs = lists:usort(string:tokens(DepsDirsSrc++DepsDirsInc, " ")), AppsDirs = lists:usort(string:tokens(AppsDirsSrc++AppsDirsInc, " ")),Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles],    Add = fun (Mod, Dep) ->         case lists:keyfind(Dep, 1, Modules) of                        false -> ok;                    {_, DepFile} ->                         {_, ModFile} = lists:keyfind(Mod, 1, Modules),                                ets:insert(E, {ModFile, DepFile}),                              digraph:add_vertex(G, Mod),                           digraph:add_vertex(G, Dep),                             digraph:add_edge(G, Mod, Dep)        end      end,    AddHd = fun (F, Mod, DepFile) ->                case file:open(DepFile, [read]) of                      {error, enoent} ->                            ok;                     {ok, Fd} ->                             {_, ModFile} = lists:keyfind(Mod, 1, Modules),                                case ets:match(E, {ModFile, DepFile}) of                                        [] ->        ets:insert(E, {ModFile, DepFile}),                                               F(F, Fd, Mod,0);                                     _ -> ok                          end             end     end,    SearchHrl = fun         F(_Hrl, []) -> {error,enoent};          F(Hrl, [Dir|Dirs]) ->                 HrlF = filename:join([Dir,Hrl]),                        case filelib:is_file(HrlF) of                true  ->                         {ok, HrlF};                             false -> F(Hrl,Dirs)                    end     end,    Attr = fun            (_F, Mod, behavior, Dep) ->                     Add(Mod, Dep);          (_F, Mod, behaviour, Dep) ->                 Add(Mod, Dep);           (_F, Mod, compile, {parse_transform, Dep}) ->                   Add(Mod, Dep);          (_F, Mod, compile, Opts) when is_list(Opts) ->                        case proplists:get_value(parse_transform, Opts) of                              undefined -> ok;                              Dep -> Add(Mod, Dep)                    end;            (F, Mod, include, Hrl) ->            case SearchHrl(Hrl, ["src", "include",AppsDir,DepsDir]++AppsDirs++DepsDirs) of                           {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl);                            {error, _} -> false                     end;            (F, Mod, include_lib, Hrl) ->        case SearchHrl(Hrl, ["src", "include",AppsDir,DepsDir]++AppsDirs++DepsDirs) of                           {ok, FoundHrl} -> AddHd(F, Mod, FoundHrl);                            {error, _} -> false                     end;            (F, Mod, import, {Imp, _}) ->        IsFile =                         case lists:keyfind(Imp, 1, Modules) of                                  false -> false;              {_, FilePath} -> filelib:is_file(FilePath)                               end,                    case IsFile of                       false -> ok;                             true -> Add(Mod, Imp)                   end;            (_, _, _, _) -> ok      end,    MakeDepend = fun              (F, Fd, Mod, StartLocation) ->                  {ok, Filename} = file:pid2name(Fd),                     case io:parse_erl_form(Fd, undefined, StartLocation) of                               {ok, AbsData, EndLocation} ->                        case AbsData of                                          {attribute, _, Key, Value} ->                                                Attr(F, Mod, Key, Value),                                                        F(F, Fd, Mod, EndLocation);                          _ -> F(F, Fd, Mod, EndLocation)                                  end;                            {eof, _ } -> file:close(Fd);         {error, ErrorDescription } ->                                    file:close(Fd);                         {error, ErrorInfo, ErrorLocation} ->                                  F(F, Fd, Mod, ErrorLocation)                    end,                    ok      end,    [beginMod = list_to_atom(filename:basename(F, ".erl")),               {ok, Fd} = file:open(F, [read]),                MakeDepend(MakeDepend, Fd, Mod,0)     end || F <- ErlFiles],  Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))),     CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)],       TargetPath = fun(Target) ->  case lists:keyfind(Target, 1, Modules) of                        false -> "";                    {_, DepFile} ->                      DirSubname = tl(string:tokens(filename:dirname(DepFile), "/")),                          string:join(DirSubname ++ [atom_to_list(Target)], "/")                end     end,    ok = file:write_file("gradualizer.d", [         [[F, "::", [[" ", D] || D <- Deps], "; @touch $@\n"] || {F, Deps} <- Depend],         "\nCOMPILE_FIRST +=", [[" ", TargetPath(CF)] || CF <- CompileFirst], "\n"       ]),     halt()' -- erlang.mk
set -x; make --no-print-directory app-build
+ make --no-print-directory app-build
erlang.mk:30: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
set -x; mkdir -p ebin/
+ mkdir -p ebin/
set -x; erlc -v +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard -o ebin/ -pa ebin/ -I include/ src/absform.erl src/constraints.erl src/gradualizer.erl src/gradualizer_cli.erl src/gradualizer_db.erl src/gradualizer_file_utils.erl src/gradualizer_lib.erl src/rebar_prv_gradualizer.erl src/ty.erl src/typechecker.erl src/typelib.erl
+ erlc -v +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard -o ebin/ -pa ebin/ -I include/ src/absform.erl src/constraints.erl src/gradualizer.erl src/gradualizer_cli.erl src/gradualizer_db.erl src/gradualizer_file_utils.erl src/gradualizer_lib.erl src/rebar_prv_gradualizer.erl src/ty.erl src/typechecker.erl src/typelib.erl
src/ty.erl:6: Warning: export_all flag enabled - all functions will be exported
src/typechecker.erl:23: Warning: export_all flag enabled - all functions will be exported
set -x; if [ -z "$(grep -e '^[^%]*{\s*modules\s*,' src/gradualizer.app.src)" ]; then \
        echo "Empty modules entry not found in gradualizer.app.src. Please consult the erlang.mk README for instructions." >&2; \
        exit 1; \
fi
++ grep -e '^[^%]*{\s*modules\s*,' src/gradualizer.app.src
+ '[' -z '  {modules, []}' ']'
set -x; cat src/gradualizer.app.src \
        | sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \['absform','constraints','gradualizer','gradualizer_cli','gradualizer_db','gradualizer_file_utils','gradualizer_lib','rebar_prv_gradualizer','ty','typechecker','typelib'\]}/" \
        | sed "s/{id,[[:space:]]*\"git\"}/{id, \"\"}/" \
        > ebin/gradualizer.app
+ cat src/gradualizer.app.src
+ sed 's/{id,[[:space:]]*"git"}/{id, ""}/'
+ sed 's/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \['\''absform'\'','\''constraints'\'','\''gradualizer'\'','\''gradualizer_cli'\'','\''gradualizer_db'\'','\''gradualizer_file_utils'\'','\''gradualizer_lib'\'','\''rebar_prv_gradualizer'\'','\''ty'\'','\''typechecker'\'','\''typelib'\''\]}/'
set -x; :
+ :
set -x; mkdir -p ./ ./ ./ ./ ./
+ mkdir -p ./ ./ ./ ./ ./
set -x; rm -f /home/username/tools/Gradualizer/.erlang.mk/escript.zip
+ rm -f /home/username/tools/Gradualizer/.erlang.mk/escript.zip
set -x; cd .. && 7z a -tzip -mx=9 -mtc=off  /home/username/tools/Gradualizer/.erlang.mk/escript.zip gradualizer/ebin/*
+ cd ..
+ 7z a -tzip -mx=9 -mtc=off /home/username/tools/Gradualizer/.erlang.mk/escript.zip 'gradualizer/ebin/*'

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz (306F2),ASM,AES-NI)

Scanning the drive:
          
WARNING: No more files
gradualizer

0 files, 0 bytes

Creating archive: /home/username/tools/Gradualizer/.erlang.mk/escript.zip

Items to compress: 0

    
Files read from disk: 0
Archive size: 22 bytes (1 KiB)

Scan WARNINGS for files and folders:

gradualizer : No more files
----------------
Scan WARNINGS: 1
make: *** [escript-zip] Error 1

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

Been following the makefile at:
https://github.com/josefs/Gradualizer/blob/master/erlang.mk#L2634

And I ran across something interesting. It is going UP out of the directory, then trying to pack gradualizer as per the line at: https://github.com/josefs/Gradualizer/blob/master/Makefile#L29

PROJECT = gradualizer

However, this git repository is named Gradualizer, thus meaning it clones out as Gradualizer, and not gradualizer.

Now, renaming the directory as gradualizer (which does not follow the naming conventions of this repository) gets it to compile:

╰─➤  make escript
erlang.mk:30: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
erlang.mk:30: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
 GEN    escript-zip
 GEN    escript

╰─➤  ls -lah g*
-rwxrwxr-x. 1 username username 144K Oct 11 11:44 gradualizer
-rw-rw-r--. 1 username username   67 Oct 11 11:41 gradualizer.d

╰─➤  ./gradualizer 
Usage: gradualizer [options] [PATH...]
A type checker for Erlang/Elixir

       PATH                      Files or directories to type check
  -h,  --help                    display this help and exit
  -pa, --path-add                Add the specified directory to the beginning of
                                 the code path; see erl -pa             [string]
       --print-file              prefix error printouts with the file name the
                                 error is from
       --no-print-file           inverse of --print-file
                                  - the default behaviour
       --stop-on-first-error     stop type checking at the first error
       --no-stop-on-first-error  inverse of --stop-on-first-error
                                  - the default behaviour

So it compiled and seems to work.

So immediately that means that the erlang.mk file at https://github.com/josefs/Gradualizer/blob/master/erlang.mk is broken because it absolutely should not ever be stepping outside of the repository directory, but a greater question is how does it work for anyone else with a default clone when the repository name is Gradualizer since the clone uri is https://github.com/josefs/Gradualizer.git thus defaulting the directory name to be Gradualizer...

from gradualizer.

Zalastax avatar Zalastax commented on August 29, 2024

@OvermindDL1 I got it to work by changing the name of the directory too. I agree that it's a strange way of doing it.

Deciding on a build system is often controversial. What we have now seems to work decently so I don't think people are too keen of changing unless there is a real benefit.

from gradualizer.

josefs avatar josefs commented on August 29, 2024

@gomoripeti, I'd like to hear your take on this.

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

Deciding on a build system is often controversial. What we have now seems to work decently so I don't think people are too keen of changing unless there is a real benefit.

Yeah I don't really care what's used as long as it works. Although that erlang.mk file is rather hairy and that is definitely a bug regardless that will break the ability to, for example, use this repository as a dependency for, say, an Elixir Gradualizer mix integrator.

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

Actually I don't think this repo could be used like that anyway as make does not come on Windows so I don't think erlang.mk could be the build system anyway, hmm...

Well, a rebar3 or a mix version of the build system would be just a couple of lines as it stands (unlike the rather huge erlang.mk file...), it's fairly simple to compile as-is.

from gradualizer.

zuiderkwast avatar zuiderkwast commented on August 29, 2024

The problem concerns only when used as a dependency for another project, right?

It should definitely be possible to use this repo as a dependency for projects using rebar3. Do we even need a rebar config file to make it work? I guess it works already, but if not, we should add a small rebar config file. Has anyone tried?

It would be nice however if the Makefile would work as well, when used as a dependency for projects using the erlang.mk dependency way (i.e. download the dependency and run make inside). If possible, it would be nice to not require a dependency to rebar in this case. A simple rule just running erlc would be enough for this.

Regardless of build system, how about renaming the repo to lowercase gradualizer? It doesn't hurt that the repo has the same name as the application.

When not used as a dependency, I don't mind using rebar, but I think the make file works fine as it is for this.

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

The problem concerns only when used as a dependency for another project, right?

No, it happens when you try to build it straight from a fresh git clone into the default named directory on any filesystem that is case-sensitive.

Using something like rebar3 'would' make it significantly easier to distribute this via packaging systems like hex as well as using it as a dependency to integrate it into build tools, but that's just a bonus around the overall issue.

If possible, it would be nice to not require a dependency to rebar in this case.

As I recall you can include a rebar thunk/wrapper file to just download-and-run, but in general if someone is doing some BEAM language work (erlang/elixir/lfe/alpaca/whatever) then they probably have rebar anyway (and considering windows as well they more likely have rebar3 than make regardless).

Regardless of build system, how about renaming the repo to lowercase gradualizer? It doesn't hurt that the repo has the same name as the application.

This would be an immediate fix and I'm all for it, regardless the erlang.mk file is bugged anyway, but that would at least make clone-and-compile work.

When not used as a dependency, I don't mind using rebar, but I think the make file works fine as it is for this.

It still prevents windows users from compiling it. Multi-platform is important.

from gradualizer.

OvermindDL1 avatar OvermindDL1 commented on August 29, 2024

One con of erlang.mk is that I dont think it supports creating a hex package.

It doesn't? That seems odd considering how well hex dependencies are used in the BEAM world across a variety of languages now. Rebar3 has native support so that would be a definite boon.

from gradualizer.

Related Issues (20)

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.