1246149Ssjgmk-files
2246149Ssjg********
3246149Ssjg
4246149SsjgThe term ``mk-files`` refers to a collection of ``*.mk`` files.
5246149Ssjg
6246149SsjgYou need bmake_ or a *recent* NetBSD_ make.
7246149SsjgIf in doubt use bmake_.
8246149Ssjg
9246149SsjgIntroduction
10246149Ssjg============
11246149Ssjg
12246149SsjgMany years ago, when building large software projects, I used GNU make
13246149Ssjg(or my own patched version of it), and had developed a set of macros
14246149Ssjgto simplify developing complex build trees.
15246149Ssjg
16246149SsjgSince the early 90's my main development machines, run BSD
17246149Ssjg(NetBSD_ to be precise), and the BSD source tree is good example of a
18246149Ssjglarge software project.   It quickly became clear that
19246149Ssjg``/usr/share/mk/*.mk`` were a great model, but were quite tightly
20246149Ssjglinked to building the BSD tree.
21246149Ssjg
22246149SsjgMuch as I liked using NetBSD, my customers were more likely to be
23246149Ssjgusing SunOS, HP-UX etc, so I started on bmake_ and a portable collection
24246149Ssjgof mk-files (mk.tar.gz_).  NetBSD provided much of the original structure.
25246149Ssjg
26246149SsjgSince then I've added a lot of features to NetBSD's make and hence to
27246149Ssjgbmake which is kept closely in sync.  The mk-files however have 
28246149Ssjgdiverged quite a bit, though ideas are still picked up from NetBSD.
29246149Ssjg
30246149SsjgBasics
31246149Ssjg------
32246149Ssjg
33246149SsjgThe BSD build model is very simple.  A directory produces one
34246149Ssjgcomponent, which is generally either a library or a program.
35246149SsjgLibrary makefiles include ``lib.mk`` and programs include ``prog.mk``
36246149Ssjgand they *do the right thing*.
37246149Ssjg
38246149SsjgA simple library makefile might look like::
39246149Ssjg
40246149Ssjg	LIB = sig
41246149Ssjg
42246149Ssjg	SRCS = \
43246149Ssjg		sigaction.c \
44246149Ssjg		sigcompat.c \
45246149Ssjg		sighdl.c
46246149Ssjg
47246149Ssjg	.include <lib.mk>
48246149Ssjg
49246149Ssjga simple program makefile::
50246149Ssjg
51246149Ssjg	PROG = cat
52246149Ssjg
53246149Ssjg	SRCS = cat.c
54246149Ssjg
55246149Ssjg	.include <prog.mk>
56246149Ssjg
57246149Ssjgin such cases even the ``SRCS`` line is unnecessary as ``prog.mk``
58246149Ssjgwill default it to ``${PROG}.c``.
59246149Ssjg
60246149SsjgIt is the sensible use of defaults and the plethora of macro modifiers
61246149Ssjgprovided by bmake_ that allow simple makefiles such as the above
62246149Ssjg*just work* on many different systems.
63246149Ssjg
64246149Ssjg
65246149Ssjgmk-files
66246149Ssjg========
67246149Ssjg
68246149SsjgThis section provides a brief description of some of the ``*.mk``
69246149Ssjgfiles. 
70246149Ssjg
71246149Ssjgsys.mk
72246149Ssjg------
73246149Ssjg
74246149SsjgWhen bmake starts, it looks for ``sys.mk`` and reads it before doing
75246149Ssjganything else.  Thus, this is the place to setup the environment for
76246149Ssjgeveryone else.
77246149Ssjg
78246149SsjgIn this distribution, sys.mk avoids doing anything platform dependent.
79246149SsjgIt is quite short, and includes a number of other files (which may or
80246149Ssjgmay not exists)
81246149Ssjg
82246149Ssjgsys.env.mk
83246149Ssjg	If it exists, is expected to do things like conditioning the
84246149Ssjg	environment.  Since it will only be included by the initial
85246149Ssjg	instance of bmake, it should ``.export`` anything that
86246149Ssjg	sub-makes might need.
87246149Ssjg
88246149Ssjgexamples/sys.clean-env.mk
89246149Ssjg	An example of how to clean the environment.
90246149Ssjg	See the file for all the details::
91246149Ssjg
92246149Ssjg		.if ${MAKE_VERSION} >= 20100606 && ${.MAKE.LEVEL} == 0
93246149Ssjg		# we save any env var that starts with these
94246149Ssjg		MAKE_SAVE_ENV_PREFIX += SB MK MAKE MACHINE NEED_ CCACHE DISTCC USE_ SSH
95246149Ssjg		MAKE_SAVE_ENV_VARS += \
96246149Ssjg			PATH HOME USER LOGNAME \
97246149Ssjg			SRCTOP OBJTOP OBJROOT \
98246149Ssjg			${_env_vars}
99246149Ssjg		
100246149Ssjg		_env_vars != env | egrep '^(${MAKE_SAVE_ENV_PREFIX:ts|})' | sed 's,=.*,,'; echo
101246149Ssjg		_export_list =
102246149Ssjg		.for v in ${MAKE_SAVE_ENV_VARS:O:u}
103246149Ssjg		.if !empty($v)
104246149Ssjg		_export_list += $v
105246149Ssjg		$v := ${$v}
106246149Ssjg		.endif
107246149Ssjg		.endfor
108246149Ssjg		# now clobber the environment
109246149Ssjg		.unexport-env
110246149Ssjg
111246149Ssjg		# list of vars that we handle specially below
112246149Ssjg		_tricky_env_vars = MAKEOBJDIR
113246149Ssjg		# export our selection - sans tricky ones
114246149Ssjg		.export ${_export_list:${_tricky_env_vars:${M_ListToSkip}}}
115246149Ssjg
116246149Ssjg		# this next bit may need tweaking
117246149Ssjg		.if defined(MAKEOBJDIR)
118246149Ssjg		srctop := ${SRCTOP:U${SB_SRC:U${SB}/src}}
119246149Ssjg		objroot := ${OBJROOT:U${SB_OBJROOT:U${SB}/${SB_OBJPREFIX}}}
120246149Ssjg		# we'll take care of MACHINE below
121246149Ssjg		objtop := ${OBJTOP:U${objroot}${MACHINE}}
122246149Ssjg		.if !empty(objtop)
123246149Ssjg		# we would normally want something like (/bin/sh):
124246149Ssjg		# MAKEOBJDIR="\${.CURDIR:S,${SRCTOP},${OBJROOT}\${MACHINE},}"
125246149Ssjg		# the $$ below is how we achieve the same result here.
126246149Ssjg		# since everything saved from the environment above
127246149Ssjg		# has run through := we need to compensate for ${MACHINE}
128246149Ssjg		MAKEOBJDIR = $${.CURDIR:S,${srctop},${objtop:S,${MACHINE},\${MACHINE},},}
129246149Ssjg
130246149Ssjg		# export these as-is, and do not track...
131246149Ssjg		.export-env ${_tricky_env_vars}
132246149Ssjg		# now evaluate for ourselves
133246149Ssjg		.for v in ${_tricky_env_vars}
134246149Ssjg		$v := ${$v}
135246149Ssjg		.endfor
136246149Ssjg
137246149Ssjg		.endif
138246149Ssjg		.endif
139246149Ssjg		.endif
140246149Ssjg
141246149Ssjg
142246149Ssjghost-target.mk
143246149Ssjg	Is used to set macros like ``HOST_TARGET``, ``HOST_OS`` and
144246149Ssjg	``host_os`` which are used to find the next step.
145246149Ssjg
146246149Ssjgsys/\*.mk
147246149Ssjg	Platform specific additions, such as ``Darwin.mk`` or ``SunOS.mk``
148246149Ssjg	set things like ``HOST_LIBEXT = .dylib`` for Darwin or
149246149Ssjg	``SHLIB_FULLVERSION = ${SHLIB_MAJOR}`` for SunOS 5.
150246149Ssjg	If there is no OS specific file, ``sys/Generic.mk`` is used.
151246149Ssjg
152246149Ssjglocal.sys.mk
153246149Ssjg	Any ``local.*.mk`` file is not part of the distribution.
154246149Ssjg	This provides a hook for sites to do extra setup without
155246149Ssjg	having to edit the distributed files.
156246149Ssjg
157246149Ssjg
158246149SsjgThe above arrangement makes it easy for the mk files to be part of a
159246149Ssjgsrc tree on an NFS volume and to allow building on multiple platforms.
160246149Ssjg
161246149Ssjglib.mk
162246149Ssjg------
163246149Ssjg
164246149SsjgThis file is used to build a number of different libraries from the
165246149Ssjgsame SRCS.
166246149Ssjg
167246149Ssjglib${LIB}.a
168246149Ssjg	An archive lib of ``.o`` files, this is the default
169246149Ssjg
170246149Ssjglib${LIB}_p.a
171246149Ssjg	A profiled lib of ``.po`` files.  
172246149Ssjg	Still an archive lib, but all the objects are built with
173246149Ssjg	profiling in mind - hence the different extension.
174246149Ssjg	It is skipped if ``MKPROFILE`` is "no".
175246149Ssjg
176246149Ssjglib${LIB}_pic.a
177246149Ssjg	An archive of ``.so`` objects compiled for relocation.
178246149Ssjg	On NetBSD this is the input to ``lib${LIB}.${LD_so}``, it is
179246149Ssjg	skipped if ``MKPICLIB`` is "no".
180246149Ssjg
181246149Ssjglib${LIB}.${LD_so}
182246149Ssjg	A shared library.  The value of ``LD_so`` is very platform
183246149Ssjg	specific.  For example::
184246149Ssjg
185246149Ssjg		# SunOS 5 and most other ELF systems
186246149Ssjg		libsslfd.so.1
187246149Ssjg
188246149Ssjg		# Darwin
189246149Ssjg		libsslfd.1.dylib
190246149Ssjg
191246149Ssjg	This library will only be built if ``SHLIB_MAJOR`` has
192246149Ssjg	a value, and ``MKPIC`` is not set to "no".
193246149Ssjg
194246149SsjgThere is a lot of platform specific tweaking in ``lib.mk``, largely the
195246149Ssjgresult of the original distributions trying to avoid interfering with
196246149Ssjgthe system's ``sys.mk``. 
197246149Ssjg
198246149Ssjglibnames.mk
199246149Ssjg-----------
200246149Ssjg
201246149SsjgThis is included by both ``prog.mk`` and ``lib.mk`` and tries to
202246149Ssjginclude ``*.libnames.mk`` of which:
203246149Ssjg
204246149Ssjglocal.libnames.mk
205246149Ssjg	does not exist unless you create it.  It is a handy way for you
206246149Ssjg	to customize without touching the distributed files. 
207246149Ssjg	For example, on a test machine I needed to build openssl but
208246149Ssjg	not install it, so put the following in ``local.libnames.mk``:: 
209246149Ssjg
210246149Ssjg		.if ${host_os} == "sunos"
211246149Ssjg		LIBCRYPTO = ${OBJTOP}/openssl/lib/crypto/libcrypto${DLIBEXT}
212246149Ssjg		LIBSSL = ${OBJTOP}/openssl/lib/ssl/libssl${DLIBEXT}
213246149Ssjg		INCLUDES_libcrypto = -I${OBJ_libcrypto}
214246149Ssjg		.endif
215246149Ssjg		
216246149Ssjg	The makefile created an openssl dir in ``${OBJ_libcrypto}`` to
217246149Ssjg	gather all the headers. dpadd.mk_ did the rest.
218246149Ssjg
219246149Ssjgsjg.libnames.mk
220246149Ssjg	not part of the mk-files distribution.
221246149Ssjg
222246149Ssjghost.libnames.mk
223246149Ssjg	contains logic to find any libs named in ``HOST_LIBS`` in
224246149Ssjg	``HOST_LIBDIRS``.
225246149Ssjg
226246149SsjgEach file above gets an opportunity to define things like::
227246149Ssjg
228246149Ssjg	LIBSSLFD	?= ${OBJTOP}/ssl/lib/sslfd/libsslfd${DLIBEXT}
229246149Ssjg	INCLUDES_libsslfd = -I${SRC_libsslfd}/h -I${OBJ_libslfd}
230246149Ssjg
231246149Ssjgthese are used by dpadd.mk_ and will be explained below.
232246149Ssjg
233246149Ssjgdpadd.mk
234246149Ssjg--------
235246149Ssjg
236246149SsjgThis file looks like line noise, and is best considered read-only.
237246149SsjgHowever it provides some very useful functionality, which simplifies the build.
238246149Ssjg
239246149SsjgMakefiles can use the LIB* macros defined via libnames.mk_ or anywhere
240246149Ssjgelse in various ways::
241246149Ssjg
242246149Ssjg	# indicate that we need to include headers from LIBCRYPTO
243246149Ssjg	# this would result in ${INCLUDES_libcrypto} being added to CFLAGS.
244246149Ssjg	SRC_LIBS += ${LIBCRYPTO}
245246149Ssjg
246246149Ssjg	# indicate that libsslfd must be built already.
247246149Ssjg	# it also has the same effect as SRC_LIBS
248246149Ssjg	DPADD += ${LIBSSLFD}
249246149Ssjg
250246149Ssjg	# indicate that not only must libsslfd be built, 
251246149Ssjg	# but that we need to link with it.
252246149Ssjg	# this is almost exactly equivalent to
253246149Ssjg	# DPADD += ${LIBSSLFD}
254246149Ssjg	# LDADD += -L${LIBSSLFD:H} -lsslfd
255246149Ssjg	# and mostly serves to ensure that DPADD and LDADD are in sync.
256246149Ssjg	DPLIBS += ${LIBSSLFD}
257246149Ssjg
258246149SsjgAny library (referenced by its full path) in any of the above, is
259246149Ssjgadded to ``DPMAGIC_LIBS`` with the following results, for each lib *foo*.
260246149Ssjg
261246149SsjgSRC_libfoo
262246149Ssjg	Is set to indicate where the src for libfoo is.
263246149Ssjg	By default it is derived from ``LIBFOO`` by replacing
264246149Ssjg	``${OBJTOP}`` with ``${SRCTOP}``.
265246149Ssjg
266246149SsjgOBJ_libfoo
267246149Ssjg	Not very exciting, is just the dir where libfoo lives.
268246149Ssjg
269246149SsjgINCLUDES_libfoo
270246149Ssjg	What to add to ``CFLAGS`` to find the public headers.
271246149Ssjg	The default varies.  If ``${SRC_libfoo}/h`` exists, it is assumed
272246149Ssjg	to be the home of all public headers and thus the default is
273246149Ssjg	``-I${SRC_libfoo}/h``
274246149Ssjg
275246149Ssjg	Otherwise we make no assumptions and the default is
276246149Ssjg	``-I${SRC_libfoo} -I${OBJ_libfoo}``
277246149Ssjg
278246149SsjgLDADD_libfoo
279246149Ssjg	This only applies to libs reference via ``DPLIBS``.
280246149Ssjg	The default is ``-lfoo``, ``LDADD_*`` provides a hook to
281246149Ssjg	instantiate other linker flags at the appropriate point
282246149Ssjg	without losing the benfits of ``DPLIBS``.
283246149Ssjg
284246149Ssjgprog.mk
285246149Ssjg-------
286246149Ssjg
287246149SsjgCompiles the specified SRCS and links them and the nominated libraries
288246149Ssjginto a program.  Prog makefiles usually need to list the libraries
289246149Ssjgthat need to be linked.   We prefer use of ``DPLIBS`` but the more
290246149Ssjgtraditional ``DPADD`` and ``LDADD`` work just as well.
291246149SsjgThat is::
292246149Ssjg
293246149Ssjg	DPLIBS += ${LIBCRYPTO}
294246149Ssjg
295246149Ssjgis equivalent to::
296246149Ssjg
297246149Ssjg	DPADD += ${LIBCRYPTO}
298246149Ssjg	LDADD += -lcrypto
299246149Ssjg
300246149Ssjgobj.mk
301246149Ssjg------
302246149Ssjg
303246149SsjgOne of the cool aspects of BSD make, is its support for separating
304246149Ssjgobject files from the src tree.  This is also the source of much
305246149Ssjgconfusion to some.
306246149Ssjg
307246149SsjgTraditionally one had to do a separate ``make obj`` pass through the
308246149Ssjgtree.  If ``MKOBJDIRS`` is "auto", we include auto.obj.mk_.
309246149Ssjg
310246149Ssjgauto.obj.mk
311246149Ssjg-----------
312246149Ssjg
313246149SsjgThis leverages the ``.OBJDIR`` target introduced some years ago to
314246149SsjgNetBSD make, to automatically create the desired object dir.
315246149Ssjg
316246149Ssjgsubdir.mk
317246149Ssjg---------
318246149Ssjg
319246149SsjgThis is the traditional means of walking the tree.  A makefile sets
320246149Ssjg``SUBDIR`` to the list of sub-dirs to visit.
321246149Ssjg
322246149SsjgIf ``SUBDIR_MUST_EXIST`` is set, missing directories cause an error,
323246149Ssjgotherwise a warning is issued.  If you don't even want the warning,
324246149Ssjgset ``MISSING_DIR=continue``.
325246149Ssjg
326246149SsjgTraditionally, ``subdir.mk`` prints clue as it visits each subdir::
327246149Ssjg
328246149Ssjg	===> ssl
329246149Ssjg	===> ssl/lib
330246149Ssjg	===> ssl/lib/sslfd
331246149Ssjg
332246149Ssjgyou can suppress that - or enhance it by setting ``ECHO_DIR``::
333246149Ssjg
334246149Ssjg	# suppress subdir noise
335246149Ssjg	ECHO_DIR=:
336246149Ssjg	# print time stamps
337246149Ssjg	ECHO_DIR=echo @ `date "+%s [%Y-%m-%d %T] "`
338246149Ssjg
339246149Ssjglinks.mk
340246149Ssjg--------
341246149Ssjg
342246149SsjgProvides rules for processing lists of ``LINKS`` and ``SYMLINKS``.
343246149SsjgEach is expected to be a list of ``link`` and ``target`` pairs
344246149Ssjg(``link`` -> ``target``). 
345246149Ssjg
346246149SsjgThe logic is generally in a ``_*_SCRIPT`` which is referenced in a
347246149Ssjg``_*_USE`` (``.USE``) target.
348246149Ssjg
349246149SsjgThe ``_BUILD_*`` forms are identical, but do not use ``${DESTDIR}``
350246149Ssjgand so are useful for creating symlinks during the build phase.
351246149SsjgFor example::
352246149Ssjg
353246149Ssjg	SYMLINKS += ${.CURDIR}/${MACHINE_ARCH}/include machine
354246149Ssjg	header_links: _BUILD_SYMLINKS_USE
355246149Ssjg	
356246149Ssjg	md.o: header_links
357246149Ssjg
358246149Ssjgwould create a symlink called ``machine`` in ``${.OBJDIR}`` pointing to
359246149Ssjg``${.CURDIR}/${MACHINE_ARCH}/include`` before compiling ``md.o``
360246149Ssjg
361246149Ssjg
362246149Ssjgautoconf.mk
363246149Ssjg-----------
364246149Ssjg
365246149SsjgDeals with running (or generating) GNU autoconf ``configure`` scripts.
366246149Ssjg
367246149Ssjgdep.mk
368246149Ssjg------
369246149Ssjg
370246149SsjgDeals with collecting dependencies.  Another useful feature of BSD
371246149Ssjgmake is the separation of this sort of information into a ``.depend``
372246149Ssjgfile.  ``MKDEP`` needs to point to a suitable tool (like mkdeps.sh_)
373246149Ssjg
374246149SsjgIf ``USE_AUTODEP_MK`` is "yes" includes autodep.mk_
375246149Ssjg
376246149Ssjgautodep.mk
377246149Ssjg----------
378246149Ssjg
379246149SsjgLeverages the ``-MD`` feature of recent GCC to collect dependency
380246149Ssjginformation as a side effect of compilation.  With this GCC puts
381246149Ssjgdependency info into a ``.d`` file.
382246149Ssjg
383246149SsjgUnfortunately GCC bases the name of the ``.d`` file on the name of the
384246149Ssjginput rather than the output file, which causes problems when the same
385246149Ssjgsource is compiled different ways.  The latest GCC supports ``-MF`` to
386246149Ssjgname the ``.d`` file and ``-MT`` to control the name to put as the
387246149Ssjgdependent.
388246149Ssjg
389246149SsjgRecent bmake allows dependencies for the ``.END`` target (run at the
390246149Ssjgend if everything was successful), and ``autodep.mk`` uses this to
391246149Ssjgpost process the ``.d`` files into ``.depend``.
392246149Ssjg
393246149Ssjgauto.dep.mk
394246149Ssjg-----------
395246149Ssjg
396246149SsjgA much simpler implementation than autodep.mk_ it uses 
397246149Ssjg``-MF ${.TARGET:T}.d``
398246149Ssjgto avoid possible conflicts during parallel builds.  
399246149SsjgThis precludes the use of suffix rules to drive ``make depend``, so 
400246149Ssjgdep.mk_ handles that if specifically requested.
401246149Ssjg
402246149Ssjgown.mk
403246149Ssjg------
404246149Ssjg
405246149SsjgNormally included by ``init.mk`` (included by ``lib.mk`` and
406246149Ssjg``prog.mk`` etc), sets macros for default ownership  etc.
407246149Ssjg
408246149SsjgIt includes ``${MAKECONF}`` if it is defined and exists.
409246149Ssjg
410246149Ssjgman.mk
411246149Ssjg------
412246149Ssjg
413246149SsjgDeals with man pages.
414246149Ssjg
415246149Ssjgwarnings.mk
416246149Ssjg-----------
417246149Ssjg
418246149SsjgThis provides a means of fine grained control over warnings on a per
419246149Ssjg``${MACHINE}`` or even file basis.
420246149Ssjg
421246149SsjgA makefile sets ``WARNINGS_SET`` to name a list of warnings
422246149Ssjgand individual ``W_*`` macros can be used to tweak them.
423246149SsjgFor example::
424246149Ssjg
425246149Ssjg	WARNINGS_SET = HIGH
426246149Ssjg	W_unused_sparc = -Wno-unused
427246149Ssjg
428246149Ssjgwould add all the warnings in ``${HIGH_WARNINGS}`` to CFLAGS, but
429246149Ssjgon sparc, ``-Wno-unused`` would replace ``-Wunused``.
430246149Ssjg
431246149SsjgYou should never need to edit ``warnings.mk``, it will include
432246149Ssjg``warnings-sets.mk`` if it exists and you use that to make any local
433246149Ssjgcustomizations. 
434246149Ssjg
435281812Ssjgrst2htm.mk
436281812Ssjg----------
437281812Ssjg
438281812SsjgLogic to simplify generating HTML (and PDF) documents from ReStructuredText.
439281812Ssjg
440281812Ssjgcython.mk
441281812Ssjg---------
442281812Ssjg
443281812SsjgLogic to build Python C interface modules using Cython_
444281812Ssjg
445281812Ssjg.. _Cython: http://www.cython.org/
446281812Ssjg
447246149SsjgMeta mode
448246149Ssjg=========
449246149Ssjg
450246149SsjgThe 20110505 and later versions of ``mk-files`` include a number of
451281812Ssjgmakefiles contributed by Juniper Networks, Inc.  
452281812SsjgThese allow the latest version of bmake_ to run in `meta mode`_
453281812Ssjgsee `dirdeps.mk`_
454246149Ssjg
455281812Ssjg.. _`dirdeps.mk`: /help/sjg/dirdeps.htm
456246149Ssjg.. _`meta mode`: bmake-meta-mode.htm
457246149Ssjg
458246149SsjgInstall
459246149Ssjg=======
460246149Ssjg
461246149SsjgYou can use the content of mk.tar.gz_ without installing at all.
462246149Ssjg
463246149SsjgThe script ``install-mk`` takes care of copying ``*.mk`` into a
464246149Ssjgdestination directory, and unless told not to, create ``bsd.*.mk`` links
465246149Ssjgfor ``lib.mk`` etc.
466246149Ssjg
467246149SsjgIf you just want to create the ``bsd.*.mk`` links in the directory
468246149Ssjgwhere you unpacked the tar file, you can::
469246149Ssjg
470246149Ssjg	./mk/install-mk ./mk
471246149Ssjg
472246149Ssjg------
473246149Ssjg
474246149Ssjg.. _bmake: bmake.htm
475246149Ssjg.. _NetBSD: http://www.netbsd.org/
476246149Ssjg.. _mkdeps.sh: http://www.crufty.net/ftp/pub/sjg/mkdeps.sh
477246149Ssjg.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
478246149Ssjg
479246149Ssjg:Author: sjg@crufty.net
480281812Ssjg:Revision: $Id: mk-files.txt,v 1.16 2014/09/05 04:41:16 sjg Exp $
481246149Ssjg:Copyright: Crufty.NET
482